]> Frank Brehm's Git Trees - pixelpark/pp-admin-tools.git/commitdiff
Adding scripts/disabling-ldap-user
authorFrank Brehm <frank.brehm@pixelpark.com>
Mon, 7 Mar 2022 15:13:04 +0000 (16:13 +0100)
committerFrank Brehm <frank.brehm@pixelpark.com>
Mon, 7 Mar 2022 15:13:04 +0000 (16:13 +0100)
scripts/disabling-ldap-user [new file with mode: 0755]

diff --git a/scripts/disabling-ldap-user b/scripts/disabling-ldap-user
new file mode 100755 (executable)
index 0000000..89e5b73
--- /dev/null
@@ -0,0 +1,922 @@
+#!/bin/bash
+
+set -e
+set -u
+
+VERBOSE="n"
+DEBUG="n"
+QUIET='n'
+SIMULATE='n'
+YES="n"
+
+VERSION="1.1"
+
+# console colors:
+RED=""
+YELLOW=""
+GREEN=""
+# BLUE=""
+CYAN=""
+NORMAL=""
+
+BASENAME=$(basename "${0}")
+BASE_DIR=$( dirname "$0" )
+cd "${BASE_DIR}" || exit 99
+BASE_DIR=$( readlink -f . )
+
+declare -a INSTANCES=()
+INSTANCES+=( 'dpx-legacy' )
+INSTANCES+=( 'dpx-prd' )
+INSTANCES+=( 'dpx-dev' )
+INSTANCES+=( 'spk-prd' )
+INSTANCES+=( 'spk-dev' )
+
+declare -a USED_INSTANCES=()
+
+declare -A LDAP_URLS=()
+LDAP_URLS['dpx-legacy']="ldap://ldap-legacy.pixelpark.com"
+LDAP_URLS['dpx-prd']="ldaps://prd-ds.pixelpark.com"
+LDAP_URLS['dpx-dev']="ldaps://dev-ldap2.pixelpark.com"
+LDAP_URLS['spk-prd']="ldaps://live-ldap.spk.pixelpark.net"
+LDAP_URLS['spk-dev']="ldaps://stage-ldap.spk.pixelpark.net"
+
+declare -A BIND_DNS=()
+BIND_DNS['dpx-legacy']="cn=admin"
+BIND_DNS['dpx-prd']="cn=admin"
+BIND_DNS['dpx-dev']="cn=admin"
+BIND_DNS['spk-prd']="cn=admin"
+BIND_DNS['spk-dev']="cn=admin"
+
+declare -A BIND_PW_FILES=()
+BIND_PW_FILES['dpx-legacy']="${HOME}/.private/ldap-admin-wonl.txt"
+BIND_PW_FILES['dpx-prd']="${HOME}/.private/dirsrv-dpx-prd-admin-pwd-wonl.txt"
+BIND_PW_FILES['dpx-dev']="${HOME}/.private/dirsrv-dpx-dev-admin-pwd-wonl.txt"
+BIND_PW_FILES['spk-prd']="${HOME}/.private/dirsrv-mngr-spk-live.pwd-wonl.txt"
+BIND_PW_FILES['spk-dev']="${HOME}/.private/dirsrv-mngr-spk-stage.pwd-wonl.txt"
+
+declare -A SUFFIXES=()
+SUFFIXES['dpx-legacy']="o=isp"
+SUFFIXES['dpx-prd']="o=isp"
+SUFFIXES['dpx-dev']="o=isp"
+SUFFIXES['spk-prd']="dc=spk,dc=pixelpark,dc=net"
+SUFFIXES['spk-dev']="dc=spk,dc=pixelpark,dc=net"
+
+declare -a USERS=()
+
+LDIF_FILE=
+
+USER=
+INSTANCE=
+LDAP_URL=
+BIND_DN=
+BIND_PW_FILE=
+SUFFIX=
+USER_DN=
+USER_CN=
+USER_MAIL=
+USER_NAME=
+
+# none
+EMPTY_PASSWD="CWEQxQmmAOSuI"
+
+CURRENT_TS=$( date +'%s' )
+SHADOW_EXPIRE=$( echo "( ${CURRENT_TS} / 3600 / 24 ) - 100" | bc )
+
+NEW_LOGIN_SHELL="/bin/false"
+
+#-------------------------------------------------------------------
+detect_color() {
+
+    local safe_term="${TERM//[^[:alnum:]]/?}"
+    local match_lhs=""
+    local use_color="false"
+    local term=
+
+    if [[ -f ~/.dir_colors   ]] ; then
+        match_lhs="${match_lhs}$( grep '^TERM ' ~/.dir_colors | sed -e 's/^TERM  *//' -e 's/ .*//')"
+    fi
+    if [[ -f /etc/DIR_COLORS   ]] ; then
+        match_lhs="${match_lhs}$( grep '^TERM ' /etc/DIR_COLORS | sed -e 's/^TERM  *//' -e 's/ .*//')"
+    fi
+    if [[ -z ${match_lhs} ]] ; then
+        type -P dircolors >/dev/null && \
+        match_lhs=$(dircolors --print-database | grep '^TERM ' | sed -e 's/^TERM  *//' -e 's/ .*//')
+    fi
+    for term in ${match_lhs} ; do
+        # shellcheck disable=SC2053
+        if [[ "${safe_term}" == ${term} || "${TERM}" == ${term} ]] ; then
+            use_color="true"
+            break
+        fi
+    done
+
+    # console colors:
+    if [ "${use_color}" = "true" ] ; then
+        RED="\\033[38;5;196m"
+        YELLOW="\\033[38;5;226m"
+        GREEN="\\033[38;5;46m"
+        # BLUE="\\033[38;5;27m"
+        CYAN="\\033[38;5;36m"
+        NORMAL="\\033[39m"
+        # HAS_COLORS="y"
+    else
+        RED=""
+        YELLOW=""
+        GREEN=""
+        # BLUE=""
+        CYAN=""
+        NORMAL=""
+    fi
+
+    local my_tty
+
+    my_tty=$(tty)
+    if [[ "${my_tty}" =~ 'not a tty' ]] ; then
+        my_tty='-'
+    fi
+
+}
+detect_color
+
+#------------------------------------------------------------------------------
+my_date() {
+    date +'%F %T.%N %:::z'
+}
+
+#------------------------------------------------------------------------------
+debug() {
+    if [[ "${VERBOSE}" != "y" ]] ; then
+        return 0
+    fi
+    echo -e " * [$(my_date)] [${BASENAME}:${CYAN}DEBUG${NORMAL}]: $*" >&2
+}
+
+#------------------------------------------------------------------------------
+info() {
+    if [[ "${QUIET}" == "y" ]] ; then
+        return 0
+    fi
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        echo -e " ${GREEN}*${NORMAL} [$(my_date)] [${BASENAME}:${GREEN}INFO${NORMAL}] : $*"
+    else
+        echo -e " ${GREEN}*${NORMAL} $*"
+    fi
+}
+
+#------------------------------------------------------------------------------
+warn() {
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        echo -e " ${YELLOW}*${NORMAL} [$(my_date)] [${BASENAME}:${YELLOW}WARN${NORMAL}] : $*" >&2
+    else
+        echo -e " ${YELLOW}*${NORMAL} [${BASENAME}:${YELLOW}WARN${NORMAL}] : $*" >&2
+    fi
+}
+
+#------------------------------------------------------------------------------
+error() {
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        echo -e " ${RED}*${NORMAL} [$(my_date)] [${BASENAME}:${RED}ERROR${NORMAL}]: $*" >&2
+    else
+        echo -e " ${RED}*${NORMAL} [${BASENAME}:${RED}ERROR${NORMAL}]: $*" >&2
+    fi
+}
+
+#------------------------------------------------------------------------------
+description() {
+    cat <<-EOF
+
+       Disables a LDAP User by Setting its inetStatus to '${CYAN}disabled${NORMAL}',
+       setting the Login Shell to '${CYAN}/bin/false${NORMAL}', setting the password to a dummy value
+       and removing the appropriate entries in groups.
+
+       EOF
+
+}
+
+#------------------------------------------------------------------------------
+draw_line() {
+    if [[ "${QUIET}" == "y" ]] ; then
+        return 0
+    fi
+    echo "---------------------------------------------------"
+}
+
+#------------------------------------------------------------------------------
+empty_line() {
+    if [[ "${QUIET}" == "y" ]] ; then
+        return 0
+    fi
+    echo
+}
+
+#------------------------------------------------------------------------------
+usage() {
+
+    local inst_out=""
+    local inst=
+    local i=0
+
+    for inst in "${INSTANCES[@]}" ; do
+        i=$(( $i + 1 ))
+        if [[ "${i}" != "1" ]] ; then
+            if [[ "${i}" == "${#INSTANCES[*]}" ]] ; then
+                inst_out+=" and "
+            else
+                inst_out+=", "
+            fi
+        fi
+        inst_out+="'${CYAN}${inst}${NORMAL}'"
+    done
+
+    cat <<-EOF
+       Usage: ${BASENAME} [[-I|--instance INSTANCE] -I ...] [-s|--simulate] [-d|--debug] [[-v|--verbose] | [-q|--quiet]] [--nocolor] USER1 [USER2 ...]
+              ${BASENAME} [-h|--help]
+              ${BASENAME} [-V|--version]
+
+           The mandatory users to disable are the POSIX user names (in LDAP: 'uid'), e.g. '${CYAN}max.mustermann${NORMAL}'.
+
+           Options:
+               -I|--instance INSTANCE
+                               The LDAP instance to use as the target of this script. If left empty, all instances are changed.
+                               May be one or more of ${inst_out}.
+               -s|--simulate   Simulation mode, nothing is really done.
+               -d|--debug      Debug output (bash -x).
+               -v|--verbose    Set verbosity on.
+               -q|--quiet      Quiet execution. Mutually exclusive to --verbose.
+               --nocolor       Don't use colors on display.
+               -h|--help       Show this output and exit.
+               -V|--version    Prints out version number of the script and exit.
+
+       EOF
+
+}
+
+#------------------------------------------------------------------------------
+get_options() {
+
+    local tmp=
+    local short_options="I:sydvqhV"
+    local long_options="inst:,instance:,simulate,yes,debug,verbose,quiet,help,version"
+    local py_version=
+    local py_found="n"
+    local ret=
+    local inst=
+
+    set +e
+    tmp=$( getopt -o "${short_options}" --long "${long_options}" -n "${BASENAME}" -- "$@" )
+    ret="$?"
+    if [[ "${ret}" != 0 ]] ; then
+        echo "" >&2
+        echo -e "$( usage )" >&2
+        exit 1
+    fi
+    set -e
+
+    # Note the quotes around `$TEMP': they are essential!
+    eval set -- "${tmp}"
+
+    while true ; do
+        case "$1" in
+            -I|--inst|--instance)
+                USED_INSTANCES+=("$2" )
+                shift
+                shift
+                ;;
+            -s|--simulate)
+                SIMULATE="y"
+                shift
+                ;;
+            -y|--yes)
+                YES="y"
+                shift
+                ;;
+            -d|--debug)
+                DEBUG="y"
+                shift
+                ;;
+            -v|--verbose)
+                VERBOSE="y"
+                shift
+                ;;
+            -q|--quiet)
+                QUIET="y"
+                RED=""
+                YELLOW=""
+                GREEN=""
+                # BLUE=""
+                CYAN=""
+                NORMAL=""
+                # HAS_COLORS="n"
+                shift
+                ;;
+            --nocolor)
+                RED=""
+                YELLOW=""
+                GREEN=""
+                # BLUE=""
+                CYAN=""
+                NORMAL=""
+                # HAS_COLORS="n"
+                shift
+                ;;
+            -h|--help)
+                echo -e "$( description )"
+                echo
+                echo -e "$( usage )"
+                exit 0
+                ;;
+            -V|--version)
+                echo "${BASENAME} version: ${VERSION}"
+                exit 0
+                ;;
+            --) shift
+                break
+                ;;
+            *)  echo "Internal error!"
+                exit 1
+                ;;
+        esac
+    done
+
+    while [[ "$#" != "0" ]] ; do
+        USERS+=( "$1" )
+        shift
+    done
+
+    if [[ "${DEBUG}" = "y" ]] ; then
+        set -x
+    fi
+
+    if [[ "${VERBOSE}" == "y" && "${QUIET}" == "y" ]] ; then
+        error "Options '${RED}--verbose${NORMAL}' and '${RED}--quiet${NORMAL}' are mutually exclusive."
+        echo >&2
+        echo -e "$( usage )" >&2
+        exit 1
+    fi
+
+    if [[ "${#USED_INSTANCES[*]}" == 0 ]] ; then
+        for inst in "${INSTANCES[@]}" ; do
+            USED_INSTANCES+=( "${inst}" )
+        done
+    fi
+
+    local instances_ok='y'
+    for inst in "${USED_INSTANCES[@]}" ; do
+        if [[ ! -v "LDAP_URLS[${inst}]" ]] ; then
+            instances_ok='n'
+            error "Given instance '${RED}${inst}${NORMAL}' is invalid."
+        fi
+    done
+    if [[ "${instances_ok}" == "n" ]] ; then
+        echo >&2
+        echo -e "$( usage )" >&2
+        exit 1
+    fi
+
+    if [[ "${#USERS[*]}" == "0" ]] ; then
+        echo >&2
+        warn "No user to disable given."
+        echo >&2
+        echo -e "$( usage )" >&2
+        exit 1
+    fi
+
+    if [[ "${SIMULATE}" == "y" ]] ; then
+        echo
+        echo -e "${CYAN}---------------------------------------------${NORMAL}"
+        echo -e "           ${YELLOW}Simulation mode${NORMAL}"
+        echo -e "      ${CYAN}Nothing will be done in real.${NORMAL}"
+        echo -e "${CYAN}---------------------------------------------${NORMAL}"
+        sleep 0.5
+    fi
+
+}
+
+#------------------------------------------------------------------------------
+RM() {
+
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        rm --verbose "$@"
+    else
+        rm "$@"
+    fi
+
+}
+
+#------------------------------------------------------------------------------
+cleanup_tmp_file() {
+    if [[ -n "${LDIF_FILE}" ]] ; then
+        if [[ -e "${LDIF_FILE}" ]] ; then
+            debug "Removing temporary file '${CYAN}${LDIF_FILE}${NORMAL}' ..."
+            RM --force --recursive "${LDIF_FILE}"
+        fi
+    fi
+}
+
+#------------------------------------------------------------------------------
+yes_or_no() {
+
+    local msg="$1"
+    local timeout="5"
+    if [[ "$#" -gt 1 ]] ; then
+        timeout="$2"
+    fi
+
+    if [[ "${YES}" == "y" ]] ; then
+        return 0
+    fi
+
+    local answer=
+    debug "Trying to get an answer with a timeout of ${timeout} seconds."
+    printf "${msg}"
+    if read -t "${timeout}" answer ; then
+        debug "Got an answer: '${answer}'"
+    else
+        echo
+        return 1
+    fi
+
+    local first_letter=$( echo "${answer}" | tr '[:upper:]' '[:lower:]' | sed -e 's/^\(.\).*/\1/' )
+    if [[ "${first_letter}" == 'y' || "${first_letter}" == 'j' ]] ; then
+        return 0
+    else
+        return 1
+    fi
+
+}
+
+#------------------------------------------------------------------------------
+get_user_dn() {
+
+    debug "Trying to detect the DN uf user '${CYAN}${USER}${NORMAL}' ..."
+
+    if [[ ! -f "${BIND_PW_FILE}" ]] ; then
+        error "Did not found password file '${RED}${BIND_PW_FILE}${NORMAL}' of Bind DN '${CYAN}${BIND_DN}${NORMAL}' of LDAP instance '${GREEN}${INSTANCE}${NORMAL}'."
+        empty_line
+        exit 5
+    fi
+
+    USER_DN=''
+    USER_CN=
+    USER_MAIL=
+    USER_NAME=
+    filter="(&(objectClass=*)(uid=${USER}))"
+    cmd="ldapsearch -x -LLL -o ldif-wrap=no -H '${LDAP_URL}' "
+    cmd+="-b \"${SUFFIX}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\" "
+    cmd+="\"${filter}\" dn cn mail"
+    # cmd+="\"${filter}\" dn cn mail | grep '^dn:' | sed -e 's/^dn:[   ][      ]*//i' | head -n 1"
+    debug "Executing: ${cmd}"
+    local user_info=$( eval ${cmd} )
+
+    local count=$( echo "${user_info}" | grep '^dn:' | wc -l )
+    if [[ "${count}" == "0" ]] ; then
+        warn "Did not found DN of uid '${YELLOW}${USER}${NORMAL}'."
+        return 1
+    fi
+    if [[ "${count}" -gt 1 ]] ; then
+        warn "User Id '${YELLOW}${USER}${NORMAL}' is not unique."
+        return 1
+    fi
+
+    USER_DN=$( echo "${user_info}" | grep '^dn:' | sed -e 's/^dn:[     ][      ]*//i' )
+    USER_CN=$( echo "${user_info}" | grep '^cn:' )
+    if echo "${USER_CN}" | grep -q '^cn::' ; then
+        USER_CN=$( echo "${USER_CN}" | sed -e 's/^cn::[        ][      ]*//i' | base64 -d )
+    else
+        USER_CN=$( echo "${USER_CN}" | sed -e 's/^cn:[         ][      ]*//i' )
+    fi
+    USER_MAIL=$( echo "${user_info}" | grep '^mail:' | sed -e 's/^mail:[       ][      ]*//i' | head -n 1 )
+
+    USER_NAME="${USER_CN}"
+    if [[ -n "${USER_MAIL}" ]] ; then
+        USER_NAME+=" <${USER_MAIL}>"
+    fi
+
+    local question="Do you want to disable User '${CYAN}${USER_NAME}${NORMAL}' (${CYAN}${USER_DN}${NORMAL}) [${RED}y${NORMAL}|${YELLOW}N${NORMAL}] ? "
+    if yes_or_no "${question}" ; then
+        info "User '${CYAN}${USER_NAME}${NORMAL}' (${CYAN}${USER_DN}${NORMAL}) will be disabled."
+        return 0
+    fi
+    info "User '${CYAN}${USER_NAME}${NORMAL}' (${CYAN}${USER_DN}${NORMAL}) will be left untouched."
+    return 1
+
+}
+
+#------------------------------------------------------------------------------
+disabling_password() {
+
+    local old_pwd=
+    local cmd=
+    local value=
+    local locked_pwd_encoded=
+
+    info "Disabling password of '${CYAN}${USER_NAME}${NORMAL}'."
+
+    debug "Getting current hashed password of user '${CYAN}${USER}${NORMAL}' ..."
+
+    cmd="ldapsearch -x -LLL -o ldif-wrap=no -H '${LDAP_URL}' -s base "
+    cmd+="-b \"${USER_DN}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\" userPassword "
+    cmd+="| grep -i '^userPassword:' | head -n 1"
+    debug "Executing: ${cmd}"
+    value=$( eval ${cmd} )
+
+    if [[ -n "${value}" ]] ; then
+        if echo "${value}" | grep -q -i "^userPassword::" ; then
+            old_pwd=$( printf "${value}" | sed -e 's/^userPassword::[  ][      ]*//i' | base64 -d )
+        else
+            old_pwd=$( printf "${value}" | sed -e 's/^userPassword:[   ][      ]*//i' )
+        fi
+        debug "Found old password hash '${CYAN}${USER}${NORMAL}': '${CYAN}${old_pwd}${NORMAL}'."
+    else
+        debug "User '${CYAN}${USER}${NORMAL}' has currently no password."
+    fi
+
+    local empty_pwd_encoded=$( echo "${EMPTY_PASSWD}" | base64 -w 0 )
+
+    if [[ -n "${old_pwd}" ]] ; then
+        locked_pwd_encoded=$( echo "${old_pwd}" | base64 -w 0 )
+        cat > "${LDIF_FILE}" <<-EOF
+                       dn: ${USER_DN}
+                       changetype: modify
+                       add: carLicense
+                       carLicense:: ${locked_pwd_encoded}
+                       -
+                       delete: userPassword
+                       -
+                       replace: shadowExpire
+                       shadowExpire: ${SHADOW_EXPIRE}
+                       EOF
+    else
+        cat > "${LDIF_FILE}" <<-EOF
+                       dn: ${USER_DN}
+                       changetype: modify
+                       replace: shadowExpire
+                       shadowExpire: ${SHADOW_EXPIRE}
+                       EOF
+    fi
+    echo "-" >> "${LDIF_FILE}"
+    echo '' >> "${LDIF_FILE}"
+
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        debug  "Resulting LDIF:"
+        cat "${LDIF_FILE}"
+    fi
+
+    cmd="ldapmodify -H \"${LDAP_URL}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\""
+    cmd+=" -f \"$( readlink -f "${LDIF_FILE}" )\""
+    debug "Executing: ${cmd}"
+    if [[ "${SIMULATE}" != "y" ]] ; then
+        eval $cmd
+    fi
+    debug "Done."
+
+}
+
+#------------------------------------------------------------------------------
+setting_user_status() {
+
+    info "Setting UserStatus of '${CYAN}${USER_NAME}${NORMAL}' to '${CYAN}disabled${NORMAL}' ..."
+
+    cat > "${LDIF_FILE}" <<-EOF
+               dn: ${USER_DN}
+               changetype: modify
+               replace: inetUserStatus
+               inetUserStatus: inactive
+               -
+               replace: mailUserStatus
+               mailUserStatus: inactive
+               -
+
+               EOF
+
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        debug  "Resulting LDIF:"
+        cat "${LDIF_FILE}"
+    fi
+
+    cmd="ldapmodify -H \"${LDAP_URL}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\""
+    cmd+=" -f \"$( readlink -f "${LDIF_FILE}" )\""
+    debug "Executing: ${cmd}"
+    if [[ "${SIMULATE}" != "y" ]] ; then
+        eval $cmd
+    fi
+    debug "Done."
+
+}
+
+#------------------------------------------------------------------------------
+remove_uniq_member() {
+
+    local group_dn="$1"
+
+    info "Removing '${CYAN}${USER}${NORMAL}' from group '${CYAN}${group_dn}${NORMAL}' ..."
+
+    local cmd=
+
+    cat > "${LDIF_FILE}" <<-EOF
+               dn: ${group_dn}
+               changetype: modify
+               delete: uniqueMember
+               uniqueMember: ${USER_DN}
+               -
+
+               EOF
+
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        debug  "Resulting LDIF:"
+        cat "${LDIF_FILE}"
+    fi
+
+    cmd="ldapmodify -H \"${LDAP_URL}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\""
+    cmd+=" -f \"$( readlink -f "${LDIF_FILE}" )\""
+    debug "Executing: ${cmd}"
+    if [[ "${SIMULATE}" != "y" ]] ; then
+        eval $cmd
+    fi
+    debug "Done."
+
+}
+
+#------------------------------------------------------------------------------
+remove_all_uniq_member() {
+
+    empty_line
+    info "Removing all entries of kind '${CYAN}uniqueMember=${BIND_DN}${NORMAL}' ..."
+
+    local group_dn=
+    local cmd=
+
+    local oifs="${IFS}"
+    IFS="
+"
+
+    cmd="ldapsearch -x -LLL -o ldif-wrap=no -H '${LDAP_URL}' -s sub "
+    cmd+="-b \"${SUFFIX}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\" "
+    cmd+="\"(uniqueMember=${USER_DN})\" dn | "
+    cmd+="grep -i '^dn:' | sed -e 's/^dn: //i'"
+    debug "Executing: ${cmd}"
+    for group_dn in $( eval ${cmd} ) ; do
+        remove_uniq_member "${group_dn}"
+    done
+    IFS="${oifs}"
+
+}
+
+#------------------------------------------------------------------------------
+remove_member() {
+
+    local group_dn="$1"
+
+    info "Removing '${CYAN}${USER}${NORMAL}' from group '${CYAN}${group_dn}${NORMAL}' ..."
+
+    local cmd=
+
+    cat > "${LDIF_FILE}" <<-EOF
+               dn: ${group_dn}
+               changetype: modify
+               delete: member
+               member: ${USER_DN}
+               -
+
+               EOF
+
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        debug  "Resulting LDIF:"
+        cat "${LDIF_FILE}"
+    fi
+
+    cmd="ldapmodify -H \"${LDAP_URL}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\""
+    cmd+=" -f \"$( readlink -f "${LDIF_FILE}" )\""
+    debug "Executing: ${cmd}"
+    if [[ "${SIMULATE}" != "y" ]] ; then
+        eval $cmd
+    fi
+    debug "Done."
+
+}
+
+#------------------------------------------------------------------------------
+remove_all_member() {
+
+    empty_line
+    info "Removing all entries of kind '${CYAN}member=${USER_DN}${NORMAL}' ..."
+
+    local group_dn=
+    local cmd=
+
+    local oifs="${IFS}"
+    IFS="
+"
+
+    cmd="ldapsearch -x -LLL -o ldif-wrap=no -H '${LDAP_URL}' -s sub "
+    cmd+="-b \"${SUFFIX}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\" "
+    cmd+="\"(member=${USER_DN})\" dn | "
+    cmd+="grep -i '^dn:' | sed -e 's/^dn: //i'"
+    debug "Executing: ${cmd}"
+    for group_dn in $( eval ${cmd} ) ; do
+        remove_member "${group_dn}"
+    done
+    IFS="${oifs}"
+
+}
+
+#------------------------------------------------------------------------------
+remove_member_uid() {
+
+    local group_dn="$1"
+
+    info "Removing '${CYAN}${USER}${NORMAL}' from group '${CYAN}${group_dn}${NORMAL}' ..."
+
+    local cmd=
+
+    cat > "${LDIF_FILE}" <<-EOF
+               dn: ${group_dn}
+               changetype: modify
+               delete: memberUid
+               memberUid: ${USER}
+               -
+
+               EOF
+
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        debug  "Resulting LDIF:"
+        cat "${LDIF_FILE}"
+    fi
+
+    cmd="ldapmodify -H \"${LDAP_URL}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\""
+    cmd+=" -f \"$( readlink -f "${LDIF_FILE}" )\""
+    debug "Executing: ${cmd}"
+    if [[ "${SIMULATE}" != "y" ]] ; then
+        eval $cmd
+    fi
+    debug "Done."
+
+}
+
+#------------------------------------------------------------------------------
+remove_all_member_uid() {
+
+    empty_line
+    info "Removing all entries of kind '${CYAN}memberUid=${USER}${NORMAL}' ..."
+
+    local group_dn=
+    local cmd=
+
+    local oifs="${IFS}"
+    IFS="
+"
+
+    cmd="ldapsearch -x -LLL -o ldif-wrap=no -H '${LDAP_URL}' -s sub "
+    cmd+="-b \"${SUFFIX}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\" "
+    cmd+="\"(memberUid=${USER})\" dn | "
+    cmd+="grep -i '^dn:' | sed -e 's/^dn: //i'"
+    debug "Executing: ${cmd}"
+    for group_dn in $( eval ${cmd} ) ; do
+        remove_member_uid "${group_dn}"
+    done
+    IFS="${oifs}"
+
+}
+
+#------------------------------------------------------------------------------
+remove_sudo_user() {
+
+    local group_dn="$1"
+
+    info "Removing '${CYAN}${USER}${NORMAL}' from group '${CYAN}${group_dn}${NORMAL}' ..."
+
+    local cmd=
+
+    cat > "${LDIF_FILE}" <<-EOF
+               dn: ${group_dn}
+               changetype: modify
+               delete: sudoUser
+               sudoUser: ${USER}
+               -
+
+               EOF
+
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        debug  "Resulting LDIF:"
+        cat "${LDIF_FILE}"
+    fi
+
+    cmd="ldapmodify -H \"${LDAP_URL}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\""
+    cmd+=" -f \"$( readlink -f "${LDIF_FILE}" )\""
+    debug "Executing: ${cmd}"
+    if [[ "${SIMULATE}" != "y" ]] ; then
+        eval $cmd
+    fi
+    debug "Done."
+
+}
+
+#------------------------------------------------------------------------------
+remove_all_sudo_users() {
+
+    empty_line
+    info "Removing all entries of kind '${CYAN}sudoUser=${USER}${NORMAL}' ..."
+
+    local group_dn=
+    local cmd=
+
+    local oifs="${IFS}"
+    IFS="
+"
+
+    cmd="ldapsearch -x -LLL -o ldif-wrap=no -H '${LDAP_URL}' -s sub "
+    cmd+="-b \"${SUFFIX}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\" "
+    cmd+="\"(sudoUser=${USER})\" dn | "
+    cmd+="grep -i '^dn:' | sed -e 's/^dn: //i'"
+    debug "Executing: ${cmd}"
+    for group_dn in $( eval ${cmd} ) ; do
+        remove_sudo_user "${group_dn}"
+    done
+    IFS="${oifs}"
+
+}
+
+#------------------------------------------------------------------------------
+modify_login_shell() {
+
+    info "Setting Login Shell of '${CYAN}${USER_NAME}${NORMAL}' to '${CYAN}${NEW_LOGIN_SHELL}${NORMAL}' ..."
+
+    cat > "${LDIF_FILE}" <<-EOF
+               dn: ${USER_DN}
+               changetype: modify
+               replace: loginShell
+               loginShell: ${NEW_LOGIN_SHELL}
+               -
+
+               EOF
+
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        debug  "Resulting LDIF:"
+        cat "${LDIF_FILE}"
+    fi
+
+    cmd="ldapmodify -H \"${LDAP_URL}\" -x -D \"${BIND_DN}\" -y \"${BIND_PW_FILE}\""
+    cmd+=" -f \"$( readlink -f "${LDIF_FILE}" )\""
+    debug "Executing: ${cmd}"
+    if [[ "${SIMULATE}" != "y" ]] ; then
+        eval $cmd
+    fi
+    debug "Done."
+
+}
+
+#------------------------------------------------------------------------------
+perform_instance() {
+
+    empty_line
+    draw_line
+    info "Disabling user '${GREEN}${USER}${NORMAL}' in LDAP instance '${GREEN}${INSTANCE}${NORMAL}'."
+    empty_line
+    sleep 2
+
+    if get_user_dn ; then
+        :
+    else
+        return 0
+    fi
+
+    debug "Continue ..."
+    setting_user_status
+    disabling_password
+    modify_login_shell
+    remove_all_uniq_member
+    remove_all_member
+    remove_all_member_uid
+    remove_all_sudo_users
+
+}
+
+#------------------------------------------------
+main() {
+
+    get_options "$@"
+
+    debug "Creating temporary LDIF file ..."
+    LDIF_FILE=$( mktemp disable-user.XXXXXXXX.ldif )
+    debug "Temporary file is '${CYAN}${LDIF_FILE}${NORMAL}'."
+
+    trap cleanup_tmp_file INT TERM EXIT ABRT
+
+    for USER in "${USERS[@]}" ; do
+        for INSTANCE in "${USED_INSTANCES[@]}" ; do
+            LDAP_URL="${LDAP_URLS[${INSTANCE}]}"
+            BIND_DN="${BIND_DNS[${INSTANCE}]}"
+            BIND_PW_FILE="${BIND_PW_FILES[${INSTANCE}]}"
+            SUFFIX="${SUFFIXES[${INSTANCE}]}"
+            perform_instance
+        done
+    done
+
+    empty_line
+    info "Finished."
+
+}
+
+main "$@"
+
+# vim: et list