mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-04 15:54:48 +01:00
Merge remote-tracking branch 'fspoettel/admin-panel-2fa' into totp
# Conflicts: # management/auth.py # management/daemon.py # management/mailconfig.py # setup/mail-users.sh
This commit is contained in:
@@ -29,7 +29,7 @@ create_user() {
|
||||
local email="$1"
|
||||
local pass="${2:-$email}"
|
||||
local priv="${3:-test}"
|
||||
local totpVal="${4:-}" # "secret,token"
|
||||
local totpVal="${4:-}" # "secret,token,label"
|
||||
local localpart="$(awk -F@ '{print $1}' <<< "$email")"
|
||||
local domainpart="$(awk -F@ '{print $2}' <<< "$email")"
|
||||
#local uid="$localpart"
|
||||
@@ -47,12 +47,13 @@ create_user() {
|
||||
local totpObjectClass=""
|
||||
local totpSecret="$(awk -F, '{print $1}' <<< "$totpVal")"
|
||||
local totpMruToken="$(awk -F, '{print $2}' <<< "$totpVal")"
|
||||
local totpLabel="$(awk -F, '{print $3}' <<< "$totpVal")"
|
||||
if [ ! -z "$totpVal" ]; then
|
||||
local nl=$'\n'
|
||||
totpObjectClass="${nl}objectClass: totpUser"
|
||||
totpSecret="${nl}totpSecret: ${totpSecret}"
|
||||
[ ! -z "$totpMruToken" ] && \
|
||||
totpMruToken="${nl}totpMruToken: ${totpMruToken}"
|
||||
totpSecret="${nl}totpSecret: {0}${totpSecret}"
|
||||
totpMruToken="${nl}totpMruToken: {0}${totpMruToken}"
|
||||
totpLabel="${nl}totpLabel: {0}${totpLabel}"
|
||||
fi
|
||||
|
||||
ldapadd -H "$LDAP_URL" -x -D "$LDAP_ADMIN_DN" -w "$LDAP_ADMIN_PASSWORD" >>$TEST_OF 2>&1 <<EOF
|
||||
@@ -66,7 +67,7 @@ sn: $localpart
|
||||
displayName: $localpart
|
||||
mail: $email
|
||||
maildrop: $email
|
||||
mailaccess: $priv${totpSecret}${totpMruToken}
|
||||
mailaccess: $priv${totpSecret}${totpMruToken}${totpLabel}
|
||||
userPassword: $(slappasswd_hash "$pass")
|
||||
EOF
|
||||
[ $? -ne 0 ] && die "Unable to add user $dn (as admin)"
|
||||
|
||||
@@ -218,6 +218,18 @@ mgmt_get_totp_token() {
|
||||
return 1
|
||||
}
|
||||
|
||||
mgmt_mfa_status() {
|
||||
local user="$1"
|
||||
local pw="$2"
|
||||
record "[Get MFA status]"
|
||||
if ! mgmt_rest_as_user "GET" "/admin/mfa/status" "$user" "$pw"; then
|
||||
REST_ERROR="Failed: GET /admin/mfa/status: $REST_ERROR"
|
||||
return 1
|
||||
fi
|
||||
# json is in REST_OUTPUT...
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
mgmt_totp_enable() {
|
||||
# enable TOTP for user specified
|
||||
@@ -228,17 +240,17 @@ mgmt_totp_enable() {
|
||||
|
||||
local user="$1"
|
||||
local pw="$2"
|
||||
local label="$3" # optional
|
||||
TOTP_SECRET=""
|
||||
|
||||
record "[Enable TOTP for $user]"
|
||||
|
||||
# 1. get a totp secret
|
||||
if ! mgmt_rest_as_user "GET" "/admin/mfa/status" "$user" "$pw"; then
|
||||
REST_ERROR="Failed: GET/admin/mfa/status: $REST_ERROR"
|
||||
if ! mgmt_mfa_status "$user" "$pw"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
TOTP_SECRET="$(/usr/bin/jq -r ".totp_secret" <<<"$REST_OUTPUT")"
|
||||
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
|
||||
@@ -255,9 +267,9 @@ mgmt_totp_enable() {
|
||||
return 2
|
||||
fi
|
||||
|
||||
# 3. enable TOTP
|
||||
# 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"; then
|
||||
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
|
||||
@@ -288,13 +300,41 @@ mgmt_assert_totp_enable() {
|
||||
}
|
||||
|
||||
|
||||
mgmt_totp_disable() {
|
||||
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"
|
||||
record "[Disable TOTP for $user]"
|
||||
if ! mgmt_rest_as_user "POST" "/admin/mfa/totp/disable" "$user" "$pw"
|
||||
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/totp/disable: $REST_ERROR"
|
||||
REST_ERROR="Failed: POST /admin/mfa/disable: $REST_ERROR"
|
||||
return 1
|
||||
else
|
||||
record "Success"
|
||||
@@ -302,12 +342,12 @@ mgmt_totp_disable() {
|
||||
fi
|
||||
}
|
||||
|
||||
mgmt_assert_totp_disable() {
|
||||
mgmt_assert_mfa_disable() {
|
||||
local user="$1"
|
||||
mgmt_totp_disable "$@"
|
||||
mgmt_mfa_disable "$@"
|
||||
local code=$?
|
||||
if [ $code -ne 0 ]; then
|
||||
test_failure "Unable to disable TOTP for $user: $REST_ERROR"
|
||||
test_failure "Unable to disable MFA for $user: $REST_ERROR"
|
||||
return 1
|
||||
fi
|
||||
get_attribute "$LDAP_USERS_BASE" "(&(mail=$user)(objectClass=totpUser))" "dn"
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
# Access assertions:
|
||||
# service accounts, except management:
|
||||
# can bind but not change passwords, including their own
|
||||
# can read all attributes of all users but not userPassword, totpSecret, totpMruToken
|
||||
# can read all attributes of all users but not userPassword, totpSecret, totpMruToken, totpLabel
|
||||
# can not write any user attributes, including shadowLastChange
|
||||
# can read config subtree (permitted-senders, domains)
|
||||
# no access to services subtree, except their own dn
|
||||
# users:
|
||||
# can bind and change their own password
|
||||
# can read and change their own shadowLastChange
|
||||
# no read or write access to user's own totpSecret or totpMruToken
|
||||
# no read or write access to user's own totpSecret, totpMruToken or totpLabel
|
||||
# can read attributess of all users except:
|
||||
# mailaccess, totpSecret, totpMruToken
|
||||
# mailaccess, totpSecret, totpMruToken, totpLabel
|
||||
# no access to config subtree
|
||||
# no access to services subtree
|
||||
# other:
|
||||
@@ -38,24 +38,25 @@ test_user_change_password() {
|
||||
|
||||
|
||||
test_user_access() {
|
||||
# 1. can read attributess of all users except mailaccess, totpSecret, totpMruToken
|
||||
# 1. can read attributess of all users except mailaccess, totpSecret, totpMruToken, totpLabel
|
||||
# 2. can read and change their own shadowLastChange
|
||||
# 3. no access to config subtree
|
||||
# 4. no access to services subtree
|
||||
# 5. no read or write access to own totpSecret or totpMruToken
|
||||
# 5. no read or write access to own totpSecret, totpMruToken, or totpLabel
|
||||
|
||||
test_start "user-access"
|
||||
|
||||
local totpSecret="12345678901234567890"
|
||||
local totpMruToken="94287082"
|
||||
local totpLabel="my phone"
|
||||
|
||||
# create regular user's alice and bob
|
||||
local alice="alice@somedomain.com"
|
||||
create_user "alice@somedomain.com" "alice" "" "$totpSecret,$totpMruToken"
|
||||
create_user "alice@somedomain.com" "alice" "" "$totpSecret,$totpMruToken,$totpLabel"
|
||||
local alice_dn="$ATTR_DN"
|
||||
|
||||
local bob="bob@somedomain.com"
|
||||
create_user "bob@somedomain.com" "bob" "" "$totpSecret,$totpMruToken"
|
||||
create_user "bob@somedomain.com" "bob" "" "$totpSecret,$totpMruToken,$totpLabel"
|
||||
local bob_dn="$ATTR_DN"
|
||||
|
||||
# alice should be able to set her own shadowLastChange
|
||||
@@ -64,27 +65,27 @@ test_user_access() {
|
||||
# test that alice can read her own attributes
|
||||
assert_r_access "$alice_dn" "$alice_dn" "alice" read mail maildrop cn sn shadowLastChange
|
||||
|
||||
# alice should not have access to her own mailaccess, totpSecret or totpMruToken, though
|
||||
assert_r_access "$alice_dn" "$alice_dn" "alice" no-read mailaccess totpSecret totpMruToken
|
||||
# alice should not have access to her own mailaccess, totpSecret, totpMruToken or totpLabel, though
|
||||
assert_r_access "$alice_dn" "$alice_dn" "alice" no-read mailaccess totpSecret totpMruToken totpLabel
|
||||
|
||||
# test that alice cannot change her own select attributes
|
||||
assert_w_access "$alice_dn" "$alice_dn" "alice"
|
||||
|
||||
# test that alice cannot change her own totpSecret or totpMruToken
|
||||
assert_w_access "$alice_dn" "$alice_dn" "alice" no-write "totpSecret=ABC" "totpMruToken=123456"
|
||||
# test that alice cannot change her own totpSecret, totpMruToken or totpLabel
|
||||
assert_w_access "$alice_dn" "$alice_dn" "alice" no-write "totpSecret=ABC" "totpMruToken=123456" "totpLabel=x-phone"
|
||||
|
||||
|
||||
# test that alice can read bob's attributes
|
||||
assert_r_access "$bob_dn" "$alice_dn" "alice" read mail maildrop cn sn
|
||||
|
||||
# alice should not have access to bob's mailaccess, totpSecret, or totpMruToken
|
||||
assert_r_access "$bob_dn" "$alice_dn" "alice" no-read mailaccess totpSecret totpMruToken
|
||||
# alice should not have access to bob's mailaccess, totpSecret, totpMruToken, or totpLabel
|
||||
assert_r_access "$bob_dn" "$alice_dn" "alice" no-read mailaccess totpSecret totpMruToken totpLabel
|
||||
|
||||
# test that alice cannot change bob's select attributes
|
||||
assert_w_access "$bob_dn" "$alice_dn" "alice"
|
||||
|
||||
# test that alice cannot change bob's attributes
|
||||
assert_w_access "$bob_dn" "$alice_dn" "alice" no-write "totpSecret=ABC" "totpMruToken=123456"
|
||||
assert_w_access "$bob_dn" "$alice_dn" "alice" no-write "totpSecret=ABC" "totpMruToken=123456" "totpLabel=x-phone"
|
||||
|
||||
|
||||
# test that alice cannot read a service account's attributes
|
||||
@@ -151,10 +152,11 @@ test_service_access() {
|
||||
|
||||
local totpSecret="12345678901234567890"
|
||||
local totpMruToken="94287082"
|
||||
local totpLabel="my phone"
|
||||
|
||||
# create regular user with password "alice"
|
||||
local alice="alice@somedomain.com"
|
||||
create_user "alice@somedomain.com" "alice" "" "$totpSecret,$totpMruToken"
|
||||
create_user "alice@somedomain.com" "alice" "" "$totpSecret,$totpMruToken,$totpLabel"
|
||||
|
||||
# create a test service account
|
||||
create_service_account "test" "test"
|
||||
@@ -174,12 +176,12 @@ test_service_access() {
|
||||
# check that service account can read user attributes
|
||||
assert_r_access "$alice_dn" "$LDAP_POSTFIX_DN" "$LDAP_POSTFIX_PASSWORD" read mail maildrop uid cn sn shadowLastChange
|
||||
|
||||
# service account should not be able to read user's userPassword, totpSecret or totpMruToken
|
||||
assert_r_access "$alice_dn" "$LDAP_POSTFIX_DN" "$LDAP_POSTFIX_PASSWORD" no-read userPassword totpSecret totpMruToken
|
||||
# service account should not be able to read user's userPassword, totpSecret, totpMruToken, or totpLabel
|
||||
assert_r_access "$alice_dn" "$LDAP_POSTFIX_DN" "$LDAP_POSTFIX_PASSWORD" no-read userPassword totpSecret totpMruToken totpLabel
|
||||
|
||||
# service accounts cannot change user attributes
|
||||
assert_w_access "$alice_dn" "$LDAP_POSTFIX_DN" "$LDAP_POSTFIX_PASSWORD"
|
||||
assert_w_access "$alice_dn" "$LDAP_POSTFIX_DN" "$LDAP_POSTFIX_PASSWORD" no-write "shadowLastChange=1" "totpSecret=ABC" "totpMruToken=333333"
|
||||
assert_w_access "$alice_dn" "$LDAP_POSTFIX_DN" "$LDAP_POSTFIX_PASSWORD" no-write "shadowLastChange=1" "totpSecret=ABC" "totpMruToken=333333" "totpLabel=x-phone"
|
||||
fi
|
||||
|
||||
# service accounts can read config subtree (permitted-senders, domains)
|
||||
|
||||
@@ -215,7 +215,11 @@ test_totp() {
|
||||
|
||||
# alice must be admin to use TOTP
|
||||
if ! have_test_failures; then
|
||||
mgmt_assert_privileges_add "$alice" "admin"
|
||||
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)
|
||||
@@ -227,7 +231,7 @@ test_totp() {
|
||||
# logging in with just the password should now fail
|
||||
if ! have_test_failures; then
|
||||
record "Expect a login failure..."
|
||||
mgmt_assert_admin_me "$alice" "$alice_pw" "missing_token"
|
||||
mgmt_assert_admin_me "$alice" "$alice_pw" "missing-totp-token"
|
||||
fi
|
||||
|
||||
|
||||
@@ -251,7 +255,7 @@ test_totp() {
|
||||
|
||||
# ensure the totpMruToken was changed in LDAP
|
||||
get_attribute "$LDAP_USERS_BASE" "(mail=$alice)" "totpMruToken"
|
||||
if [ "$ATTR_VALUE" != "$TOTP_TOKEN" ]; then
|
||||
if [ "$ATTR_VALUE" != "{0}$TOTP_TOKEN" ]; then
|
||||
record_search "(mail=$alice)"
|
||||
test_failure "totpMruToken wasn't updated in LDAP"
|
||||
fi
|
||||
@@ -268,7 +272,7 @@ test_totp() {
|
||||
# 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_totp_disable "$alice" "$api_key"; then
|
||||
if mgmt_assert_mfa_disable "$alice" "$api_key"; then
|
||||
mgmt_assert_admin_me "$alice" "$alice_pw" "ok"
|
||||
fi
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user