#!/bin/sh # # Purpose {{{ # This script will create homedir for members of an LDAP group # 1. Get members list from LDAP group given as argument # … # # 2021-11-19 # }}} # Vars {{{ readonly PROGNAME=$(basename "${0}") readonly PROGDIR=$(readlink -m $(dirname "${0}")) readonly ARGS="${*}" readonly NBARGS="${#}" [ -z "${DEBUG}" ] && DEBUG=1 ## Export DEBUG for sub-script export DEBUG # Default values for some vars readonly LDAP_GROUP_BASE_DEFAULT="ou=grouper,dc=univ-rennes1,dc=fr" readonly LDAP_SERVER_DEFAULT="ldap://ldap.univ-rennes1.fr" if [ -f /etc/nslcd.conf ]; then readonly LDAP_PASSWD_CMD_DEFAULT=$(sed -n 's/\(^bindpw \)\(.*\)/\2/p' /etc/nslcd.conf) readonly LDAP_USER_CMD_DEFAULT=$(sed -n 's/\(^binddn \)\(.*\)/\2/p' /etc/nslcd.conf) fi readonly HOME_BASE_DEFAULT="/home" ## Colors readonly PURPLE='\033[1;35m' readonly RED='\033[0;31m' readonly RESET='\033[0m' readonly COLOR_DEBUG="${PURPLE}" # }}} usage() { # {{{ cat <<- EOF usage: $PROGNAME [-b|-d|-g|-h|-p|-s|-u] Create homedir for members of the given LDAP group. EXAMPLES : - Create homedir for members of "ldap:group:my_group" ${PROGNAME} --group "ldap:group:my_group" - Use default SSSD user for ldap requests ${PROGNAME} --user-cmd "sed -n 's/\(^ldap_default_bind_dn = \)\(.*\)/\2/p' /etc/sssd/sssd.conf" OPTIONS : -b,--base LDAP_BASE Set different LDAP base (default: ${LDAP_GROUP_BASE_DEFAULT}). -d,--debug Enable debug messages. -g,--group LDAP_GROUP_CN Required. LDAP group to parse in order to get the list of homedir to create to create. -h,--home,--home-base Base directory to use for user's home directory (default: ${HOME_BASE_DEFAULT}). --help Print this help message. -p,--password,--password-cmd "sed -n … /etc/…" Command to get LDAP bind password from a file (by default, works with NSLCD − /etc/nslcd.conf). -s,--server ldap://ldap.domain.tld LDAP url to use for ldapsearch request (default: ${LDAP_SERVER_DEFAULT}). -u,--user,--user-cmd "sed -n … /etc/…" Command to get LDAP bind user from a file (by default, works with NSLCD − /etc/nslcd.conf). EOF } # }}} debug_message() { # {{{ local_message="${1}" ## Print message if DEBUG is enable (=0) [ "${DEBUG}" -eq "0" ] && printf '\e[1;35m%-6b\e[m\n' "DEBUG − ${PROGNAME} : ${local_message}" return 0 } # }}} error_message() { # {{{ local_error_message="${1}" local_error_code="${2}" ## Print error message printf '%b\n' "ERROR − ${PROGNAME} : ${RED}${local_error_message}${RESET}" exit "${local_error_code:=66}" } # }}} define_vars() { # {{{ # If ldap_group_cn wasn't defined (argument) {{{ if [ -z "${ldap_group_cn}" ]; then ## Keep it empty ldap_group_cn="" fi # }}} # If ldap_group_base wasn't defined (argument) {{{ if [ -z "${ldap_group_base}" ]; then ## Use default value ldap_group_base="${LDAP_GROUP_BASE_DEFAULT}" fi # }}} # If ldap_server wasn't defined (argument) {{{ if [ -z "${ldap_server}" ]; then ## Use default value ldap_server="${LDAP_SERVER_DEFAULT}" fi # }}} # If ldap_passwd wasn't defined (argument) {{{ if [ -z "${ldap_passwd}" ]; then ## Use default command ldap_passwd="${LDAP_PASSWD_CMD_DEFAULT}" debug_message "Use default command to get LDAP password." fi # }}} # If ldap_user wasn't defined (argument) {{{ if [ -z "${ldap_user}" ]; then ## Use default command ldap_user="${LDAP_USER_CMD_DEFAULT}" debug_message "Use default command to get LDAP user." fi # }}} # If home_base wasn't defined (argument) {{{ if [ -z "${home_base}" ]; then ## Use default value home_base="${HOME_BASE_DEFAULT}" fi # }}} ## Temp file vars {{{ readonly ldap_user_list_path="/tmp/${PROGNAME}.ldap.user.list" readonly ldap_previous_user_list_path="/tmp/.${PROGNAME}.previous.ldap.user.list" ## }}} } # }}} is_var_empty() { # {{{ ## Return False by default return_var_empty="1" ## Total number of variables to test local_total_var="${#}" loop_count_var_empty="0" ## While it remains a variable to test while [ "${local_total_var}" -gt "${loop_count_var_empty}" ]; do debug_message "is_var_empty − \ Test var: ${1}." ### Test if this is empty and set return value to True [ -z "${1}" ] && return_var_empty="0" ### Increase the number of tested variables loop_count_var_empty=$((loop_count_var_empty+1)) ### Shift to the next variable shift done return "${return_var_empty}" } # }}} is_var_empty_silent() { # {{{ ## Return False by default return_var_empty_silent="1" ## Total number of variables to test local_total_var="${#}" loop_count_var_empty_silent="0" ## While it remains a variable to test while [ "${local_total_var}" -gt "${loop_count_var_empty_silent}" ]; do ### Test if this is empty and set return value to True [ -z "${1}" ] && return_var_empty_silent="0" ### Increase the number of tested variables loop_count_var_empty_silent=$((loop_count_var_empty_silent+1)) ### Shift to the next variable shift done return "${return_var_empty_silent}" } # }}} is_command_available() { # {{{ local_command_available_cmd="${1}" ## Return False by default return_command_available="1" if [ "$(command -v ${local_command_available_cmd})" ]; then debug_message "is_command_available − \ ${RED}${local_command_available_cmd}${COLOR_DEBUG} seems present on this host." return_command_available="0" else debug_message "is_command_available − \ ${RED}${local_command_available_cmd}${COLOR_DEBUG} is not available on this host." return_command_available="1" fi return "${return_command_available}" } # }}} is_directory_absent() { # {{{ local_directory_absent="${1}" ## Directory doesn't exists by default return_is_directory_absent="0" ### Check if the directory exists # shellcheck disable=SC2086 if test -d "${local_directory_absent}"; then return_is_directory_absent="1" debug_message "is_directory_absent − \ The directory ${RED}${local_directory_absent}${COLOR_DEBUG} exists." else return_is_directory_absent="0" debug_message "is_directory_absent − \ The directory ${RED}${local_directory_absent}${COLOR_DEBUG} doesn't exist." fi return "${return_is_directory_absent}" } # }}} get_ldap_user_list() { # {{{ ## Return False by default return_get_ldap_user_list="1" debug_message "get_ldap_user_list − \ Create or empty ${RED}${ldap_user_list_path}${COLOR_DEBUG} file to store user list of ${RED}${ldap_group_cn}${COLOR_DEBUG} LDAP group." true > "${ldap_user_list_path}" if command ldapsearch -ZZ -D "${ldap_user}" -w "${ldap_passwd}" -H "${ldap_server}" -s one -b "${ldap_group_base}" "(cn=${ldap_group_cn})" member \ | sed -n 's/\(^member: uid=\)\(.*\)\(,ou=.*\)/\2/p' \ | sort > "${ldap_user_list_path}"; then if [ -s "${ldap_user_list_path}" ]; then debug_message "get_ldap_user_list − \ ${RED}${ldap_group_cn}${COLOR_DEBUG} users list successfully created (see ${ldap_user_list_path} file)." command chmod 0400 -- "${ldap_user_list_path}" return_get_ldap_user_list="0" else debug_message "get_ldap_user_list − \ Error, the users list of ${ldap_group_cn} is empty (${ldap_user_list_path} file)." return_get_ldap_user_list="1" fi else debug_message "get_ldap_user_list − \ Error in ${RED}ldapsearch${COLOR_DEBUG} command for ${ldap_group_cn} LDAP group." return_get_ldap_user_list="1" fi return "${return_get_ldap_user_list}" } # }}} is_file_present() { # {{{ local_file_present="${1}" ## File doesn't exist by default return_is_file_present="1" ### Check if the file exists # shellcheck disable=SC2086 if find ${local_file_present} > /dev/null 2>&1; then return_is_file_present="0" debug_message "is_file_present − \ The file ${RED}${local_file_present}${COLOR_DEBUG} exists." else return_is_file_present="1" debug_message "is_file_present − \ The file ${RED}${local_file_present}${COLOR_DEBUG} doesn't exist." fi return "${return_is_file_present}" } # }}} is_file_similar() { # {{{ local_similar_file_one="${1}" local_similar_file_two="${2}" ## Files aren't similar by default doesn't exist by default return_is_file_similar="1" if diff --brief -- "${local_similar_file_one}" "${local_similar_file_two}" > /dev/null; then debug_message "is_file_similar − \ ${local_similar_file_one} and ${local_similar_file_two} are ${RED}similar${COLOR_DEBUG}." return_is_file_similar="0" else debug_message "is_file_similar − \ ${local_similar_file_one} and ${local_similar_file_two} are ${RED}NOT${COLOR_DEBUG} similar." return_is_file_similar="1" fi return "${return_is_file_similar}" } # }}} create_directory() { # {{{ local_directory="${1}" ## Directory creation fail by default return_create_directory="1" if mkdir --parents -- "${local_directory}" > /dev/null; then debug_message "create_directory − \ Successfully create ${RED}${local_directory}${COLOR_DEBUG} directory." return_create_directory="0" else debug_message "create_directory − \ Error in 'mkdir' command for ${RED}${local_directory}${COLOR_DEBUG} directory." return_create_directory="1" fi return "${return_create_directory}" } # }}} fix_directory_permissions() { # {{{ local_directory="${1}" local_username="${2}" local_user_groupname="${3}" ## Setting new permissions fail by default return_fix_directory_permissions="1" if chown --silent "${local_username}:${local_user_groupname}" -- "${local_directory}" 2> /dev/null; then debug_message "fix_directory_permissions − \ Successfully fix permissions for ${RED}${local_directory}${COLOR_DEBUG} (user: ${local_username})." return_fix_directory_permissions="0" else debug_message "fix_directory_permissions − \ Error in 'chown' command for ${RED}${local_directory}${COLOR_DEBUG} directory (user: ${local_username})." return_fix_directory_permissions="1" fi return "${return_fix_directory_permissions}" } # }}} main() { # {{{ ## If ldapsearch command is not available {{{ ### exit with message and error is_command_available "ldapsearch" \ || error_message "ldapsearch command doesn't seems to be available. Please install ldap-utils package." "3" ## }}} ## Define all vars define_vars ## If ldap_group_cn is empty {{{ ### Print help message ### AND exit with message and error is_var_empty "${ldap_group_cn}" \ && usage \ && error_message "Please enter a GROUP with -g|--group option." 1 ## }}} ## If ldap_user or ldap_passwd is empty {{{ ### Print help message ### AND exit with message and error is_var_empty_silent "${ldap_user}" "${ldap_passwd}" \ && usage \ && error_message "LDAP user or password is empty. Please verify your configuration or the --user-cmd|--passwd-cmd options." 2 ## }}} ## If home_base directory doesn't exists {{{ ### AND exit with message and error is_directory_absent "${home_base}" \ && error_message "Home base directory (${home_base}) doesn't exists. Check your configuration or use -h|--home option." 3 ## }}} ## Try to get the user list of LDAP group {{{ ### OR Exit get_ldap_user_list \ || error_message "Can't get the user list of ${ldap_group_cn} LDAP group. Please use --debug option." 4 ## }}} ## If a previous list of users exists {{{ ### If the two lists are the same ### Exit is_file_present "${ldap_previous_user_list_path}" \ && is_file_similar "${ldap_user_list_path}" "${ldap_previous_user_list_path}" \ && debug_message "main − No new user from previous run, no more actions required." \ && exit 0 ## }}} ## Parse users list {{{ while IFS= read -r username; do user_groupname=$(id --group -- "${username}" \ || error_message "Can't get the primary group uid for ${username} user." 6) ### Create user's home directory {{{ ### OR Exit create_directory "${home_base}/${username}" \ || error_message "Can't create home directory (${home_base}/${username}) for ${username} user. Please use --debug option." 5 ### }}} ### Fix permissions for this directory {{{ ### OR Exit fix_directory_permissions "${home_base}/${username}" "${username}" "${user_groupname}" \ || error_message "Can't fix permissions for ${username} home directory (path: ${home_base}/${username}, username: ${username}, group: ${user_groupname}. Please use --debug option." 7 ### }}} done < "${ldap_user_list_path}" ## }}} ## Information message debug_message "Create home directory in ${RED}${home_base}${COLOR_DEBUG}, \ for all members of ${RED}${ldap_group_cn},${ldap_group_base}${COLOR_DEBUG} LDAP group \ from ${RED}${ldap_server}${COLOR_DEBUG} LDAP server ." ## Rename user list for next run of the script ## AND exit mv --force -- "${ldap_user_list_path}" "${ldap_previous_user_list_path}" \ && exit 0 } # }}} # Manage arguments # {{{ # This code can't be in a function due to argument management if [ ! "${NBARGS}" -eq "0" ]; then manage_arg="0" ## If the first argument is not an option if ! printf -- '%s' "${1}" | grep -q -E -- "^-+"; then ## Print help message and exit printf '%b\n' "${RED}Invalid option: ${1}${RESET}" printf '%b\n' "---" usage exit 1 fi # Parse all options (start with a "-") one by one while printf -- '%s' "${1}" | grep -q -E -- "^-+"; do case "${1}" in -b|--base ) ## Define ldap_group_base ## Move to the next argument shift ## Define var readonly ldap_group_base="${1}" ;; -d|--debug ) ## debug DEBUG=0 ;; -g|--group ) ## Define ldap_group_cn ## Move to the next argument shift ## Define var readonly ldap_group_cn="${1}" ;; -h|--home|--home-base ) ## Define home_base ## Move to the next argument shift ## Define var readonly home_base="${1}" ;; --help ) ## help usage ## Exit after help informations exit 0 ;; -p|--password|--password-cmd ) ## Define ldap_passwd ## Move to the next argument shift ## Define var readonly ldap_passwd="${1}" ;; -s|--server) ## Define ldap_server ## Move to the next argument shift ## Define var readonly ldap_server="${1}" ;; -u|--user|--user-cmd ) ## Define ldap_user ## Move to the next argument shift ## Define var readonly ldap_user="${1}" ;; * ) ## unknow option printf '%b\n' "${RED}Invalid option: ${1}${RESET}" printf '%b\n' "---" usage exit 1 ;; esac debug_message "Arguments management − \ ${RED}${1}${COLOR_DEBUG} option managed." ## Move to the next argument shift manage_arg=$((manage_arg+1)) done debug_message "Arguments management − \ ${RED}${manage_arg}${COLOR_DEBUG} argument(s) successfully managed." else debug_message "Arguments management − \ No arguments/options to manage." fi # }}} main exit 255