# vim: set filetype=sh ts=4 sw=4 sts=4 et:
# shellcheck shell=bash
# shellcheck disable=SC2317,SC2086,SC2016,SC2046
# below: convoluted way that forces shellcheck to source our caller
# shellcheck source=tests/functional/launch_tests_on_instance.sh
. "$(dirname "${BASH_SOURCE[0]}")"/dummy

testsuite_mfa()
{
    # create account4
    success a0_create_a4 $a0 --osh accountCreate --always-active --account $account4 --uid $uid4 --public-key "\"$(cat $account4key1file.pub)\""
    json .error_code OK .command accountCreate .value null

    # set account4 as mfa password required
    success a0_accountModify_passreq_a4 $a0 --osh accountModify --account $account4 --mfa-password-required yes
    json .error_code OK .command accountModify .value.mfa_password_required.error_code OK

    # set account4 as mfa password required (dupe)
    success a0_accountModify_passreq_a4_dupe $a0 --osh accountModify --account $account4 --mfa-password-required yes
    json .error_code OK .command accountModify .value.mfa_password_required.error_code OK_NO_CHANGE

    # now try to connect with account4
    run a4_connect_with_passreq $a4 --osh groupList
    retvalshouldbe 122
    json .error_code KO_MFA_PASSWORD_SETUP_REQUIRED

    # setup our password, step1
    run a4_setup_pass_step1of2 $a4f --osh selfMFASetupPassword --yes
    retvalshouldbe 124
    contain 'enter this:'
    local a4_password_tmp
    a4_password_tmp=$(get_stdout | grep -Eo 'enter this: [a-zA-Z0-9_-]+' | sed -e 's/enter this: //')

    # setup our password, step2
    local a4_password
    a4_password=']BkL>3x#T)g~~B#rLv^!T2&N'
    script a4_setup_pass_step2of2 "echo 'set timeout $default_timeout;
        spawn $a4 --osh selfMFASetupPassword --yes;
        expect \":\" { sleep 0.2; send \"$a4_password_tmp\\n\"; };
        expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
        expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
        expect eof;
        lassign [wait] pid spawnid value value;
        exit \$value' | expect -f -"
    retvalshouldbe 0
    unset a4_password_tmp
    nocontain 'enter this:'
    nocontain 'unchanged'
    nocontain 'sorry'
    json .command selfMFASetupPassword .error_code OK

    # now try to connect after we have a pass
    run a4_connect_after_pass $a4f --osh groupList
    if [ "${capabilities[mfa]}" = 1 ] || [ "${capabilities[mfa-password]}" = 1 ]; then
        # now we need a password, we don't enter it so it'll timeout (124)
        retvalshouldbe 124
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'
        nocontain 'JSON_OUTPUT'
    else
        # our system doesn't support MFA so it still works without asking for a password
        retvalshouldbe 0
        nocontain 'Multi-Factor Authentication enabled'
        nocontain REGEX 'Password:|Password for'
        json .command groupList .error_code OK_EMPTY
    fi

    # batch trying to start a plugin that requires mfa => should get an error

    if [ "${capabilities[pamtester]}" = 1 ]; then

        success batch_set_mfa $r0 "echo '{\\\"mfa_required\\\":\\\"any\\\"}' \> $opt_remote_etc_bastion/plugin.info.conf \; chmod o+r $opt_remote_etc_bastion/plugin.info.conf"

        if [ "${capabilities[mfa]}" = 1 ] || [ "${capabilities[mfa-password]}" = 1 ]; then
            script batch_try_mfa "echo 'set timeout $default_timeout;
                spawn $a4 --osh batch;
                expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
                expect \"waiting for input\" { sleep 0.2; send \"info\\n\"; };
                expect \"failed\" { sleep 0.2; send \"quit\\n\"; };
                expect eof;
                lassign [wait] pid spawnid value value;
                exit \$value' | expect -f -"
            retvalshouldbe 0
            contain "launching command: info"
            contain "entering MFA phase"
            contain "please use --proactive-mfa"
            nocontain "Your alias to connect"
            json .command batch .error_code OK '.value[0].command' info '.value[0].result.error_code' KO_MFA_FAILED
        else
            script batch_try_mfa "echo 'set timeout $default_timeout;
                spawn $a4 --osh batch;
                expect \"waiting for input\" { sleep 0.2; send \"info\\n\"; };
                expect \"failed\" { sleep 0.2; send \"quit\\n\"; };
                expect eof;
                lassign [wait] pid spawnid value value;
                exit \$value' | expect -f -"
            retvalshouldbe 0
            contain "launching command: info"
            contain "entering MFA phase"
            contain "please use --proactive-mfa"
            nocontain "Your alias to connect"
            json .command batch .error_code OK '.value[0].command' info '.value[0].result.error_code' KO_MFA_FAILED
        fi

        success batch_unset_mfa $r0 "rm -f $opt_remote_etc_bastion/plugin.info.conf"
    fi

    # /batch

    if [ "${capabilities[pamtester]}" = 1 ]; then
        success a0_create_g3 $a0 --osh groupCreate --group $group3 --algo rsa --size 4096 --owner $account4

        # setup group to force JIT egress MFA
        script a4_modify_g3_egress_mfa "echo 'set timeout $default_timeout;
            spawn $a4 --osh groupModify --group $group3 --mfa-required any;
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'
        json .command groupModify .error_code OK

        # check that the MFA is set for the group
        script a4_verify_g3_egress_mfa "echo 'set timeout $default_timeout;
            spawn $a4 --osh groupInfo --group $group3;
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'
        json .command groupInfo .error_code OK
        json .value.mfa_required any

        # add 127.7.7.7 to this group
        script a4_add_g3_server "echo 'set timeout $default_timeout;
            spawn $a4 --osh groupAddServer --group $group3 --host 127.7.7.7 --user-any --port-any --force;
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'

        # connect to 127.7.7.7 with MFA JIT, bad password
        script a4_connect_g3_server_badpass "echo 'set timeout 45; \
            spawn $a4 root@127.7.7.7; \
            expect \"is required (password)\" { sleep 0.1; }; \
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; }; \
            expect \"is required (password)\" { sleep 0.1; }; \
            expect \":\" { sleep 0.2; send \"BADPASSWORD\\n\"; }; \
            expect \"is required (password)\" { sleep 0.1; }; \
            expect \":\" { sleep 0.2; send \"BADPASSWORD\\n\"; }; \
            expect \"is required (password)\" { sleep 0.1; }; \
            expect \":\" { sleep 0.2; send \"BADPASSWORD\\n\\n\"; }; \
            expect eof; \
            lassign [wait] pid spawnid value value; \
            exit \$value' | expect -f -"
        retvalshouldbe 125
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'
        contain 'pamtester: '
        nocontain 'Permission denied'

        # connect to 127.7.7.7 with MFA JIT, good password
        script a4_connect_g3_server_goodpass "echo 'set timeout $default_timeout;
            spawn $a4 root@127.7.7.7;
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect \"is required (password)\" { sleep 0.1; };
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 255
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'
        contain 'pamtester: successfully authenticated'
        contain 'Permission denied'

        # test proactive mfa
        script set_help_mfa $r0 "'"'echo \{\"mfa_required\":\ \"password\"\} > '"$opt_remote_etc_bastion"'/plugin.help.conf; chmod 644 '"$opt_remote_etc_bastion"'/plugin.help.conf'"'"
        retvalshouldbe 0

        script a4_mfa_help_jitmfa "echo 'set timeout $default_timeout;
            spawn $a4 --osh help;
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect \"is required (password)\" { sleep 0.1; };
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof; \
            lassign [wait] pid spawnid value value; \
            exit \$value' | expect -f -"
        contain 'pamtester: successfully authenticated'
        retvalshouldbe 0
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'
        nocontain 'proactive MFA'

        script a4_proactive_mfa_help "echo 'set timeout $default_timeout;
            spawn $a4 --osh help --proactive-mfa;
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect \"is required (password)\" { sleep 0.1; };
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'
        contain 'pamtester: successfully authenticated'
        contain 'proactive MFA'
        json .command help .error_code OK

        script remove_help_mfa $r0 "'"'rm -f '"$opt_remote_etc_bastion"'/plugin.help.conf'"'"
        retvalshouldbe 0
        # /proactive mfa

        # create another account
        success a0_create_a3 $a0 --osh accountCreate --always-active --account $account3 --uid $uid3 --public-key "\"$(cat $account3key1file.pub)\""
        json .error_code OK .command accountCreate .value null

        # set the account as bypass
        success a0_set_a3_as_robot $a0 --osh accountModify --account $account3 --mfa-password-required bypass
        json .command accountModify .error_code OK

        # add to JIT MFA group
        script a0_add_a3_as_member "echo 'set timeout $default_timeout;
            spawn $a4 --osh groupAddMember --group $group3 --account $account3;
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        json .command groupAddMember .error_code OK

        # connect to 127.7.7.7 with MFA JIT, no MFA needed
        run a3_connect_g3_server_mfa_bypass $a3 root@127.7.7.7
        retvalshouldbe 255
        nocontain 'pamtester: successfully authenticated'
        contain 'Permission denied'

        # remove the account bypass
        success a0_unset_a3_as_robot $a0 --osh accountModify --account $account3 --mfa-password-required no
        json .command accountModify .error_code OK

        # connect to 127.7.7.7 with MFA JIT, password setup needed
        run a3_connect_mfa_jit_need_pass_setup $a3 root@127.7.7.7
        json .error_code KO_MFA_ANY_SETUP_REQUIRED

        script a0_delete_g3 "$a0 --osh groupDelete --group $group3 <<< \"$group3\""

        script a0_delete_a3 $a0 --osh accountDelete --account $account3 "<<< \"Yes, do as I say and delete $account3, kthxbye\""
        retvalshouldbe 0
        json .command accountDelete .error_code OK
    fi

    # change our password
    a4_password_new="rkw=*Ffyqs23"
    if [ "${capabilities[mfa]}" = 1 ] || [ "${capabilities[mfa-password]}" = 1 ]; then
        script a4_change_pass "echo 'set timeout $default_timeout;
            spawn $a4 --osh selfMFASetupPassword --yes;
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect \":\" { sleep 0.2; send \"$a4_password_new\\n\"; };
            expect \":\" { sleep 0.2; send \"$a4_password_new\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'
    else
        script a4_change_pass "echo 'set timeout $default_timeout;
            spawn $a4 --osh selfMFASetupPassword --yes;
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect \":\" { sleep 0.2; send \"$a4_password_new\\n\"; };
            expect \":\" { sleep 0.2; send \"$a4_password_new\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        nocontain 'Multi-Factor Authentication enabled'
    fi
    nocontain 'enter this:'
    nocontain 'unchanged'
    json .command selfMFASetupPassword .error_code OK

    a4_password="$a4_password_new"
    unset a4_password_new

    if [ "${capabilities[mfa]}" = 1 ] || [ "${capabilities[mfa-password]}" = 1 ]; then
        script a4_connect_with_pass "echo 'set timeout $default_timeout;
            spawn $a4 --osh groupList;
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'
        json .command groupList .error_code OK_EMPTY
    fi

    # set account4 as mfa totp required
    success a0_accountModify_totpreq_a4 $a0 --osh accountModify --account $account4 --mfa-totp-required yes
    json .error_code OK .command accountModify .value.mfa_totp_required.error_code OK

    # set account4 as mfa totp required (dupe)
    success a0_accountModify_totpreq_a4_dupe $a0 --osh accountModify --account $account4 --mfa-totp-required yes
    json .error_code OK .command accountModify .value.mfa_totp_required.error_code OK_NO_CHANGE

    # now try to connect with account4
    if [ "${capabilities[mfa]}" = 1 ] || [ "${capabilities[mfa-password]}" = 1 ]; then
        script a4_connect_with_totpreq "echo 'set timeout $default_timeout;
            spawn $a4 --osh groupList;
            expect \":\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
    else
        run a4_connect_with_totpreq $a4 --osh groupList
    fi
    retvalshouldbe 123
    json .error_code KO_MFA_TOTP_SETUP_REQUIRED

    if [ "${capabilities[mfa]}" = 1 ]; then
        # setup totp
        script a4_setup_totp "echo 'set timeout $default_timeout;
            spawn $a4 --osh selfMFASetupTOTP --no-confirm;
            expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'

        local a4_totp_code_1 a4_totp_code_2
        a4_totp_code_1=$(get_stdout | grep -A1 'Your emergency scratch codes are:' | tail -n1 | tr -d '[:space:]')
        a4_totp_code_2=$(get_stdout | grep -A2 'Your emergency scratch codes are:' | tail -n1 | tr -d '[:space:]')
        #a4_totp_code_3=$(get_stdout | grep -A3 'Your emergency scratch codes are:' | tail -n1 | tr -d '[:space:]')
        #a4_totp_code_4=$(get_stdout | grep -A4 'Your emergency scratch codes are:' | tail -n1 | tr -d '[:space:]')

        # login and fail without totp (timeout)
        script a4_connect_after_totp_fail "echo 'set timeout $default_timeout;
            spawn $a4 --osh groupList;
            expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 124
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (OTP).'
        contain 'Your password expires on'
        contain 'in 89 days'
        contain REGEX 'Password:|Password for'
        contain 'Verification code:'
        nocontain 'JSON_OUTPUT'

        # success with password + totp
        script a4_connect_after_totp_ok "echo 'set timeout $default_timeout;
            spawn $a4 --osh groupList;
            expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect \"code:\" { sleep 0.2; send \"$a4_totp_code_1\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (OTP).'
        contain REGEX 'Password:|Password for'
        contain 'Verification code:'
        json .command groupList .error_code OK_EMPTY

        # totp scratch codes don't work twice
        script a4_connect_after_totp_dupe "echo 'set timeout $default_timeout;
            spawn $a4 --osh groupList;
            expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect \"code:\" { sleep 0.2; send \"$a4_totp_code_1\\n\"; };
            expect \"word:\" { exit 222; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 222
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (OTP).'
        contain REGEX 'Password:|Password for'
        contain 'Verification code:'
        nocontain 'JSON_OUTPUT'

        # set pam bypass on account4 (dupe)
        success a0_set_pambypass_a4 $a0 --osh accountModify --account $account4 --pam-auth-bypass yes
        json .error_code OK .command accountModify .value.pam_auth_bypass.error_code OK

        # set pam bypass on account4
        success a0_set_pambypass_a4_dupe $a0 --osh accountModify --account $account4 --pam-auth-bypass yes
        json .error_code OK .command accountModify .value.pam_auth_bypass.error_code OK_NO_CHANGE

        # we don't provide password or totp, it should work because bypass
        success a4_pam_auth_bypass $a4 --osh groupList
        json .command groupList .error_code OK_EMPTY

        # remove requirement of password and totp for account4, also remove bypass
        success a0_remove_mfa_req_a4 $a0 --osh accountModify --account $account4 --pam-auth-bypass no --mfa-totp-required no --mfa-password-required no
        json .error_code OK .command accountModify .value.pam_auth_bypass.error_code OK .value.mfa_totp_required.error_code OK .value.mfa_password_required.error_code OK

        # remove requirement of password and totp for account4, also remove bypass (dupe)
        success a0_remove_mfa_req_a4_dupe $a0 --osh accountModify --account $account4 --pam-auth-bypass no --mfa-totp-required no --mfa-password-required no
        json .error_code OK .command accountModify .value.pam_auth_bypass.error_code OK_NO_CHANGE .value.mfa_totp_required.error_code OK_NO_CHANGE .value.mfa_password_required.error_code OK_NO_CHANGE

        # pubkey-auth-optional

        # remove totp from account4 to simplify the following tests
        success a0_nototp_a4 $a0 --osh accountMFAResetTOTP --account $account4
        json .command accountMFAResetTOTP .error_code OK

        # pubkey-auth-optional disabled: success with pubkey and password
        script a4_no_pubkeyauthoptional_login_pubkey_pam "echo 'set timeout $default_timeout;
            spawn $a4 --osh groupList;
            expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'
        json .command groupList .error_code OK_EMPTY

        # pubkey-auth-optional disabled: fail with pubkey but no password (timeout)
        script a4_no_pubkeyauthoptional_login_pubkey_nopam $a4 --osh groupList
        retvalshouldbe 124
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain 'Your password expires on'
        contain 'in 89 days'
        contain REGEX 'Password:|Password for'
        nocontain 'JSON_OUTPUT'

        # pubkey-auth-optional disabled: fail with no pubkey (never gets to ask for the password)
        script a4_no_pubkeyauthoptional_login_nopubkey_pam $a4np --osh groupList
        retvalshouldbe 255
        contain 'Permission denied (publickey).'
        nocontain 'password'
        nocontain 'JSON_OUTPUT'

        # set pubkey-auth-optional on account4
        success a0_set_pubkeyauthoptional_a4 $a0 --osh accountModify --account $account4 --pubkey-auth-optional yes
        json .error_code OK .command accountModify .value.pubkey_auth_optional.error_code OK

        # set pubkey-auth-optional on account4 (dupe)
        success a0_set_pubkeyauthoptional_a4_dupe $a0 --osh accountModify --account $account4 --pubkey-auth-optional yes
        json .error_code OK .command accountModify .value.pubkey_auth_optional.error_code OK_NO_CHANGE

        # pubkey-auth-optional enabled: success with pubkey and password
        script a4_pubkeyauthoptional_login_pubkey_pam "echo 'set timeout $default_timeout;
            spawn $a4 --osh groupList;
            expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'
        json .command groupList .error_code OK_EMPTY

        # pubkey-auth-optional enabled: success with password only
        script a4_pubkeyauthoptional_login_nopubkey_pam "echo 'set timeout $default_timeout;
            spawn $a4np --osh groupList;
            expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain REGEX 'Password:|Password for'
        json .command groupList .error_code OK_EMPTY

        # pubkey-auth-optional enabled: fail with pubkey only
        script a4_pubkeyauthoptional_login_pubkey_nopam $a4 --osh groupList
        retvalshouldbe 124
        contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
        contain 'Your password expires on'
        contain 'in 89 days'
        contain REGEX 'Password:|Password for'
        nocontain 'JSON_OUTPUT'

        # unset pubkey-auth-optional on account4
        success a0_unset_pubkeyauthoptional_a4 $a0 --osh accountModify --account $account4 --pubkey-auth-optional no
        json .error_code OK .command accountModify .value.pubkey_auth_optional.error_code OK

        # unset mfa-any on account4 (dupe)
        success a0_unset_pubkeyauthoptional_a4_dupe $a0 --osh accountModify --account $account4 --pubkey-auth-optional no
        json .error_code OK .command accountModify .value.pubkey_auth_optional.error_code OK_NO_CHANGE

        # reset password
        script a4_reset_password "echo 'set timeout $default_timeout;
            spawn $a4 --osh selfMFAResetPassword;
            expect \"additional authentication factor is required (password)\" { sleep 0.1; };
            expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect \"additional authentication factor is required (password)\" { sleep 0.1; };
            expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
        json .error_code OK .command selfMFAResetPassword

        # now we no longer need MFA
        success a4_mfa_deconfigured $a4 --osh groupList
        json .command groupList .error_code OK_EMPTY

        # setup TOTP and see that we don't require any factor to do it
        success a4_setup_totp_nofa $a4 --osh selfMFASetupTOTP --no-confirm
        nocontain 'Multi-Factor Authentication enabled'
        a4_totp_code_1=$(get_stdout | grep -A1 'Your emergency scratch codes are:' | tail -n1 | tr -d '[:space:]')
        a4_totp_code_2=$(get_stdout | grep -A2 'Your emergency scratch codes are:' | tail -n1 | tr -d '[:space:]')

        # try to setup TOTP again and see that we do require it now (we'll timeout)
        script a4_setup_totp_fa_required "echo 'set timeout $default_timeout;
            spawn $a4 --osh selfMFASetupTOTP --no-confirm;
            expect \"additional authentication factor is required (OTP)\" { sleep 0.1; };
            expect \"code:\" { sleep 0.2; expect *; send \"$a4_totp_code_1\\n\"; };
            expect \"additional authentication factor is required (OTP)\" { sleep 0.1; };
            expect \"code:\" { sleep 0.2; expect *; send \"$a4_totp_code_2\\n\"; };
            expect eof;
            lassign [wait] pid spawnid value value;
            exit \$value' | expect -f -"
        retvalshouldbe 0
    fi

    # remove account
    script a0_delete_a4 $a0 --osh accountDelete --account $account4 "<<< \"Yes, do as I say and delete $account4, kthxbye\""
    retvalshouldbe 0
    json .command accountDelete .error_code OK
}

testsuite_mfa
unset -f testsuite_mfa
