#!/bin/bash # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 source "$(dirname -- "$0")/lib/ulib/auto" || exit 255 urequire ldif prefixes ldap ################################################################################ # Fonctions utilitaire function split_cmds() { # splitter la commande $2 sur l'un des caractères $3...$*, et mettre le # résultat dans le tableau $1. Par défaut, splitter sur // local __sc_dest="$1" __sc_cmds="$2"; shift 2 [ -n "$*" ] || set -- "//" local -a __sc_seps=("$@") local __sc_sep __sc_cmd __sc_arg __sc_foundsep __sc_addtocmd __sc_foundsep= for __sc_sep in "${__sc_seps[@]}"; do if [ "$__sc_cmds" == "$__sc_sep" -o "${__sc_cmds%$__sc_sep}" != "$__sc_cmds" ]; then __sc_foundsep=1 break fi done [ -n "$__sc_foundsep" ] || __sc_cmds="$__sc_cmds ${__sc_seps[0]}" eval "set -- $__sc_cmds" array_new "$__sc_dest" __sc_cmd= # commande actuelle for __sc_arg in "$@"; do __sc_addtocmd=1 __sc_foundsep= for __sc_sep in "${__sc_seps[@]}"; do if [ "$__sc_arg" == "$__sc_sep" ]; then __sc_addtocmd= __sc_foundsep=1 break elif [ "${__sc_arg%$__sc_sep}" != "$__sc_arg" ]; then __sc_arg="${__sc_arg%$__sc_sep}" __sc_foundsep=1 break fi done if [ -n "$__sc_addtocmd" ]; then __sc_cmd="${__sc_cmd:+$__sc_cmd }$(qvalm "$__sc_arg")" __sc_arg= fi if [ -n "$__sc_foundsep" ]; then array_add "$__sc_dest" "$__sc_cmd" __sc_cmd= fi done } ################################################################################ # Variables compute_ldap_prefixes LDAPCONF="$(expand_prefix LDAPCONFDIR)/ldap.conf" VARIABLES=(WSMODE WSAPPEND PROFILE LDAPURI SUFFIX BINDDN PASSWORD SEARCHBASE FILTER ATTRS UNCUT_ON_LOAD CUT_ON_SAVE DECODE_ON_LOAD ENCODE_ON_SAVE ENCODE_ATTRS OUTPUT EXIT_ON_ERROR EXITCODE LINES DEBUG) SPECIAL_VARIABLES=(PLAIN LDIF APPEND SEARCHBASE_EXACT) PROTECTED_VARIABLES=() # valeurs par défaut WSMODE=ldif WSAPPEND= ATTRS=() UNCUT_ON_LOAD=true CUT_ON_SAVE=true DECODE_ON_LOAD=true ENCODE_ON_SAVE=true ENCODE_ATTRS=(userPassword) EXITCODE=0 AUTOPRINT=1 INTERACTIVE=auto EXIT_ON_ERROR=auto SKIPCOUNT= LASTCMD= EDITLAST= function eval_cmdline() { # analyser la ligne $1. ce peut-être une commande ou une suite de commande # séparés par //, qui forment un seul groupe. local -a cmds local origcmd local cmd="$1" # La commande en cours va peut-être modifier la valeur de DEBUG. En # faire une sauvegarde avant local __DEBUG="$DEBUG" if [ "${cmd#$}" != "$cmd" ]; then # avec $, on exécute la commande littérale is_yes "$__DEBUG" && set -x if ! eval "${cmd#$}"; then is_yes "$EXIT_ON_ERROR" && exit 255 fi is_yes "$__DEBUG" && set +x LASTCMD="$origcmd" continue fi # sinon, ce sont des commandes de uldap origcmd="$cmd" split_cmds cmds "$cmd" "//" # splitter les commandes sur // for cmd in "${cmds[@]}"; do if eval "set -- $cmd"; then cmdalias "$1" cmd; shift set -- "${cmd[@]}" "$@" cmd="$1"; shift if [ "$cmd" == "last" ]; then EDITLAST=1 else cmd="uldap_$cmd" is_yes "$__DEBUG" && set -x if ! "$cmd" "$@"; then is_yes "$EXIT_ON_ERROR" && exit 255 fi is_yes "$__DEBUG" && set +x LASTCMD="$origcmd" fi else eerror "$cmd: erreur de syntaxe" is_yes "$EXIT_ON_ERROR" && exit 255 fi done } function set_values() { local varcmd name uname lname value local s=0 for varcmd in "$@"; do if [[ "$varcmd" == *=* ]]; then name="${varcmd%%=*}" value="${varcmd#$name=}" else name="$varcmd" value= fi uname="$(awk '{print toupper($0)}' <<<"$name")" lname="$(awk '{print tolower($0)}' <<<"$name")" if array_contains VARIABLES "$uname" || array_contains SPECIAL_VARIABLES "$uname"; then "set_$lname" "$value" || s=1 elif ! array_contains PROTECTED_VARIABLES "$name"; then _setv "$name" "$value" else s=1 fi done return $s } function set_exit_on_error() { is_yes "$1" && EXIT_ON_ERROR=true || EXIT_ON_ERROR=false } function set_wsmode() { case "$1" in plain|ldif|mod) WSMODE="$1";; *) return 1;; esac } function set_plain() { set_wsmode plain } function set_ldif() { set_wsmode ldif } function set_wsappend() { is_yes "$1" && WSAPPEND=true || WSAPPEND=false } function set_append() { set_wsappend true } function set_profile() { local profile="$1" if ! array_contains PROFILES "$profile"; then local profile_alias for profile_alias in "${PROFILE_ALIASES[@]}"; do if [ "${profile_alias%%:*}" == "$profile" ]; then profile="${profile_alias#*:}" break fi done array_contains PROFILES "$profile" || return 1 fi PROFILE="$profile" local -a profile_values array_copy profile_values "${PROFILE//-/_}_PROFILE" set_values ldapuri= suffix= binddn= password= searchbase= "${profile_values[@]}" if [ -z "$SUFFIX" -a -n "$LDAPURI" ]; then local suffix suffix="$(get_suffix "$LDAPURI")" || return 1 set_suffix "$suffix" fi [ -n "$SEARCHBASE" ] || set_searchbase "$SUFFIX" return 0 } function set_ldapuri() { LDAPURI="$1" local proto host port if ! split_ldapuri "$LDAPURI" proto host port; then LDAPURI= return 1 fi return 0 } function set_suffix() { [ -n "$BINDDN" ] && BINDDN="$(reldn "$BINDDN")" SEARCHBASE="$(reldn "$SEARCHBASE")" SUFFIX="$1" [ -n "$BINDDN" ] && BINDDN="$(absdn "$BINDDN")" SEARCHBASE="$(absdn "$SEARCHBASE")" } function set_binddn() { local auth="$1" if ! array_contains AUTH_PROFILES "$auth"; then local auth_alias for auth_alias in "${AUTH_ALIASES[@]}"; do if [ "${auth_alias%%:*}" == "$auth" ]; then auth="${auth_alias#*:}" break fi done fi if array_contains AUTH_PROFILES "$auth"; then local -a auth_values local auth_value array_copy auth_values "${auth//-/_}_AUTH" BINDDN= set_values password= for auth_value in "${auth_values[@]}"; do if [[ "$auth_value" == binddn=* ]]; then auth="${auth_value#binddn=}" if [ -z "$auth" -o "$auth" == "anonymous" ]; then BINDDN= else BINDDN="$(absdn "$auth")" fi else set_values "$auth_value" fi done elif [ -z "$auth" -o "$auth" == "anonymous" ]; then BINDDN= else BINDDN="$(absdn "$auth")" fi } function set_password() { PASSWORD="$1" } function set_searchbase_exact() { SEARCHBASE="$1" } function set_searchbase() { set_searchbase_exact "$(absdn "$1")" } function set_filter() { FILTER="$1" } function set_attrs() { array_split ATTRS "$1" } function set_uncut_on_load() { is_yes "$1" && UNCUT_ON_LOAD=true || UNCUT_ON_LOAD=false } function set_cut_on_save() { is_yes "$1" && CUT_ON_SAVE=true || CUT_ON_SAVE=false } function set_decode_on_load() { is_yes "$1" && DECODE_ON_LOAD=true || DECODE_ON_LOAD=false } function set_encode_on_save() { is_yes "$1" && ENCODE_ON_SAVE=true || ENCODE_ON_SAVE=false } function set_encode_attrs() { array_split ENCODE_ATTRS "$1" } function set_output() { OUTPUT="$1" } function set_lines() { LINES="$1" } function set_debug() { is_yes "$1" && DEBUG=true || DEBUG=false } ac_set_tmpfile TMPFILE # espace de travail temporaire ac_set_tmpfile WSFILE # espace de travail courant # le contenu de l'espace de travail est-il formatté pour ldapmodify? Dans ce # cas, aucune opération de transformation n'a à lui être appliquée WSFILES=() WSSTACK=() function clear_wsfiles() { local wsfile for wsfile in "${WSFILES[@]}"; do rm "$wsfile" done WSFILES=() WSSTACK=() WSMODE=ldif } function __push() { local __varcmd __name for __name in "$@"; do __varcmd="${__varcmd:+$__varcmd }$(echo_setv "$__name" "${!__name}")" done WSSTACK=("${WSSTACK[@]}" "$__varcmd") } function __pop() { eval "$(last_value WSSTACK)" array_del_last WSSTACK } function push_wsfile() { WSFILES=("${WSFILES[@]}" "$WSFILE") __push WSMODE WSAPPEND UNCUT_ON_LOAD CUT_ON_SAVE DECODE_ON_LOAD ENCODE_ON_SAVE ENCODE_ATTRS ac_set_tmpfile WSFILE } function pop_wsfile() { if [ -n "${WSFILES[*]}" ]; then rm "$WSFILE" WSFILE="$(last_value WSFILES)" array_del_last WSFILES __pop fi } function peek_wsfile() { last_value WSFILES } function has_wsfiles() { [ -n "${WSFILES[*]}" ] } function count_wsfiles() { echo "${#WSFILES[*]}" } ################################################################################ # Commandes COMMANDS=(help set last profile auth clear load save print alias cd list ldapsearch transform sed awk grep format sort edit diff ldapmodify ldapadd ldapdelete undo quit) COMMANDS_HELP="\ \$ cmd Passer directement une commande au shell. set [options] [var=value...] Changer des options ou des variables. set sans argument affiche la liste des variables définies. [set] plain Passer en mode 'plain': indiquer que l'espace de travail contient des données brutes. Les pré-traitements et post-traitements (uncut_on_load, decode_on_load, encode_on_save, cut_on_save) ne sont pas appliqués sur cet espace de travail [set] ldif Passer en mode 'ldif': indiquer que l'espace de travail contient des données ldif. Les pré-traitements et post-traitements sont appliqués normalement sur cet espace de travail [set] append Pour certaines opérations, spécifier si le résultat de la *prochaine* opération remplace le contenu de l'espace de travail courant (par défaut), ou si le résultat est ajouté à la fin. last Afficher en mode édition la dernière commande. Cette commande n'est fonctionnelle qu'avec une version de bash >=4.x profile name Choisir le profil 'name'. Equivalent à 'set profile=name'. Sans argument, afficher la liste des profils valides. auth anonymous|binddn [password] Spécifier le compte à utiliser pour se connecter. Equivalent à 'set binddn=binddn; set password=password' clear [-k] Vider l'espace de travail et passer en mode 'plain'. Avec l'option -k, supprimer aussi tout l'historique d'annulation. load [-k] input Charger un fichier dans l'espace de travail. Si l'extension du fichier est .ldif, passer en mode 'ldif' En mode append, rajouter le contenu du fichier à l'espace de travail, puis repasser en mode replace. Le code de retour est 0 si le fichier a été chargé, 1 sinon. save [-a] output Sauvegarder l'espace de travail dans un fichier. Avec l'option -a, rajouter au fichier au lieu de l'écraser print Afficher l'espace de travail alias a=rdn... Définir un alias pour la commande cd. 'a' est l'alias, 'rdn' est le dn correspondant, exprimé par rapport à \$suffix. Sans argument, afficher la liste des aliases définis. cd rdn Changer searchbase. Par défaut, il s'agit d'un rdn relatif à \$searchbase - Certains aliases sont supportés: .. pour l'objet parent, ~ pour \$suffix, / pour la racine. 'cd' sans argument équivaut à 'cd ~' - Si le dn commence par '~/', il s'agit d'un rdn relatif à \$suffix. - Si le dn commence par /, searchbase reçoit la valeur rdn sans modifications (sauf bien sûr enlever le '/' de tête si nécessaire). Il faut alors que ce soit un dn absolu. ls [-b searchbase] [filter [attrs...]] search [-b searchbase] [filter [attrs...]] Utiliser ldapsearch pour faire la recherche, et copier le résultat dans l'espace de travail. 'ls' est équivalent à 'search -s one'. Si ce n'est pas déjà le cas, passer en mode 'ldif'. L'option -b prend une valeur avec la même syntaxe que la commande cd, sauf que les alias ne sont pas supportés. En particulier, la valeur est relative au \$searchbase courant. Pour faire une recherche par rapport à \$suffix, il faut utiliser la syntaxe ~/searchbase. En mode append, rajouter le résultat de la recherche à l'espace de travail, puis repasser en mode replace. Le code de retour est 1 si aucun enregistrement n'a été trouvé, sinon le code de retour est celui de la commande ldapsearch. cut Couper les lignes trop longues. Cette action est en principe effectuée automatiquement lors de la sauvegarde. Il n'est pas conseillé d'appliquer des méthodes de transformation après avoir utilisé cette action. uncut Fusionner les lignes coupées. Cette action est en principe effectuée automatiquement lors du chargement ou après la recherche. encode [attrs...] Encoder en base64 les valeurs des attributs mentionnés. decode [attrs...] Décoder les valeurs des attributs mentionnés si nécessaire (c'est à dire s'ils sont encodés en base64) keepattr attrs... Garder uniquement les lignes des attributs mentionnés. Ensuite, supprimer les objets ayant uniquement la ligne dn: (en d'autres termes, keepattr sans argument supprime *tout* l'espace de travail) keepval attr patterns... Pour l'attribut attr, garder uniquement les lignes pour lesquelles les valeurs correspondent aux expressions régulières. Les autres attributs ne sont pas modifiés. Ensuite, supprimer les objets ayant uniquement la ligne dn: exclude attrs... Supprimer les lignes des attributs mentionnés. Ensuite, supprimer les objets ayant uniquement la ligne dn: excludeval attr patterns... Pour l'attribut attr, supprimer les lignes pour lesquelles les valeurs correspondent aux expressions régulières. Les autres attributs ne sont pas modifiés. Ensuite, supprimer les objets ayant uniquement la ligne dn: keepvalentry attr patterns... Pour l'attribut attr, vérifier si *au moins une* valeur correspond à l'une des expressions régulières. Si c'est le cas, garder l'entrée entière, sinon supprimer l'entrée. excludevalentry attr patterns... Pour l'attribut attr, vérifier si *aucune* des valeurs ne correspond à l'une des expressions régulières. Si c'est le cas, garder l'entrée entière, sinon supprimer l'entrée. setval attr values... Remplacer toutes les valeurs de l'attribut attr par les valeurs spécifiées. addval attr values... Ajouter un nouvel attribut avec les valeurs spécifiées. Si l'attribut existe déjà, les nouvelles valeurs sont ajoutées à la fin. sed args Modifier l'espace de travail avec le résultat de la commande sed. note: aucun argument n'est filtré, mais il ne faut pas utiliser les options de sed qui provoquent la modification en place du fichier, comme par exemple l'option -i awk args Modifier l'espace de travail avec le résultat de la commande awk. grep args Modifier l'espace de travail avec le résultat de la commande grep. format [options] attrs... Formater l'espace de travail en données tabulaires, et passer en mode 'plain'. --show-headers Afficher les en-têtes -F FSEP Spécifier le séparateur pour les attributs. Par défaut, il s'agit du caractère de tabulation. -R VSEP Spécifier le séparateur pour les valeurs des attributs. Par défaut, il s'agit du point-virgule ';' -e Retourner les valeurs comme des variables shell. Les options -F et -R sont ignorées. Les attributs multivalués sont écrits sous forme de tableaux. Par exemple: attributes=('mail' 'givenName') index=0 mail='user@domain.fr' givenName=('peter' 'gabriel') --bc Dans le mode -e, spécifier une commande à insérer avant le premier enregistrement. Quand cette commande est lancée, index==-1 -c Dans le mode -e, spécifier une commande à insérer après chaque enregistrement --ec Dans le mode -e, spécifier une commande à insérer après le dernier enregistrement sort [args] Modifier l'espace de travail avec le résultat de la commande sort. edit Lancer un éditeur pour modifier l'espace de travail. diff [options] Afficher les différences entre l'espace de travail et la version précédente ifok cmd iferror cmd Si le dernier code de retour est 0 (resp. !=0), lancer la commande cmd skip n Sauter les n prochaines commandes. A utiliser avec ifok et iferror undo Annuler la dernière modification effectuée sur l'espace de travail Les directives suivantes prennent le contenu de l'espace de travail, et le transforment en une suite de commandes de modifications pour ldapmodify: A Créer un objet de toutes pièces avec les attributs donnés et leurs valeurs. a Ajouter les valeurs spécifiée à l'attribut r Remplacer les valeurs de l'attribut par celles spécifiées d Supprimer les valeurs spécifiées de l'attribut D Supprimer l'attribut delentry Supprimer l'objet ldapmodify Utiliser ldapmodify pour modifier les objets sur le serveur. Il faut utiliser au préalable l'une des méthodes de transformation parmi A, a, r, d, D, delentry. Le code de retour est celui de la commande ldapmodify. ldapadd Utiliser ldapadd pour créer les objets situés dans l'espace de travail. Le code de retour est celui de la commande ldapadd. ldapdelete Utiliser ldapdelete pour supprimer la liste des dns situés dans l'espace de travail. Le code de retour est celui de la commande ldapdelete. Notes: - les expressions régulières sont celles reconnues par awk. - pour spécifier plusieurs actions sur une même ligne, les séparer par // - le code de retour est 0 si ok, 255 si une erreur s'est produite (erreur de syntaxe, de connexion, de lecture/écriture de fichier, etc.). sinon, les opérations ldap{search,modify,delete,add} ont leur code de retour respectifs" function cmdalias() { # soit le nom de la commande $1, mettre dans le tableau $2(=cmd) la forme # canonique de la commande, avec ses arguments éventuels local cmdalias_="$1" cmdname_="${2:-cmd}" local -a cmdvals_=("$cmdalias_") case "$cmdalias_" in h|help) cmdvals_=(help);; set) cmdvals_=(set);; plain) cmdvals_=(set wsmode=plain);; ldif) cmdvals_=(set wsmode=ldif);; append) cmdvals_=(set wsappend=true);; l|last) cmdvals_=(last);; profile) cmdvals_=(profile);; auth) cmdvals_=(auth);; clear) cmdvals_=(clear);; L|load) cmdvals_=(load);; S|save) cmdvals_=(save);; p|print) cmdvals_=(print);; alias) cmdvals_=(alias);; cd) cmdvals_=(cd);; ls|list|dir) cmdvals_=(list);; f|find|s|search|ldapsearch) cmdvals_=(ldapsearch);; cut) cmdvals_=(transform cut);; uncut) cmdvals_=(transform uncut);; enc|encode) cmdvals_=(transform encode);; dec|decode) cmdvals_=(transform decode);; av|addval) cmdvals_=(transform addval);; rv|replval|sv|setval) cmdvals_=(transform replval);; k|keep|keepattr) cmdvals_=(transform keepattr);; K|kv|keepval) cmdvals_=(transform keepval);; x|exclude|excludeattr) cmdvals_=(transform excludeattr);; kve|keepvalentry) cmdvals_=(transform keepvalentry);; xve|excludevalentry) cmdvals_=(transform excludevalentry);; dv|delval|X|xv|excludeval) cmdvals_=(transform excludeval);; xempty|excludeempty) cmdvals_=(transform excludeempty);; sed) cmdvals_=(sed);; awk) cmdvals_=(awk);; grep) cmdvals_=(grep);; format) cmdvals_=(format);; sort) cmdvals_=(sort);; e|edit) cmdvals_=(edit);; A|modaddattr) cmdvals_=(transform modaddattr);; a|ma|modadd|modaddval) cmdvals_=(transform modaddval);; r|mr|modrepl|modreplval) cmdvals_=(transform modreplval);; d|md|moddel|moddelval) cmdvals_=(transform moddelval);; D|moddelattr) cmdvals_=(transform moddelattr);; delentry|moddelentry) cmdvals_=(transform moddelentry);; mod|modify|ldapmodify|commit|ci) cmdvals_=(ldapmodify);; ldapadd) cmdvals_=(ldapadd);; ldapdelete) cmdvals_=(ldapdelete);; ifok) cmdvals_=(ifok);; iferror) cmdvals_=(iferror);; skip) cmdvals_=(skip);; u|undo|revert) cmdvals_=(undo);; q|exit|quit) cmdvals_=(quit);; esac set_array "$cmdname_" @ "${cmdvals_[@]}" } function uldap_help() { <<<"$COMMANDS_HELP" sed 's/^ //' } function uldap_set() { local args varcmd name lname uname local -a varcmds values if parse_opts \ -C: '$varcmds=("${varcmds[@]}" profile="$value_")' \ -H: '$varcmds=("${varcmds[@]}" ldapuri="$value_")' \ -D: '$varcmds=("${varcmds[@]}" binddn="$value_")' \ -w: '$varcmds=("${varcmds[@]}" password="$value_")' \ -b: '$varcmds=("${varcmds[@]}" searchbase="$value_")' \ --nu '$varcmds=("${varcmds[@]}" uncut_on_load=false)' \ --nc '$varcmds=("${varcmds[@]}" cut_on_save=false)' \ -u '$varcmds=("${varcmds[@]}" uncut_on_load=true cut_on_save=true)' \ --nd '$varcmds=("${varcmds[@]}" decode_on_load= encode_on_save=)' \ -d '$varcmds=("${varcmds[@]}" decode_on_load=true encode_on_save=true encode_attrs="userPassword")' \ -e '$varcmds=("${varcmds[@]}" exit_on_error=true)' \ --ne '$varcmds=("${varcmds[@]}" exit_on_error=false)' \ @ args -- "$@"; then varcmds=("${varcmds[@]}" "${args[@]}") else eerror "$args" return 1 fi set -- "${varcmds[@]}" if [ -n "$*" ]; then for varcmd in "$@"; do if [[ "$varcmd" == *=* ]]; then name="${varcmd%%=*}" value="${varcmd#$name=}" else name="$varcmd" value= fi case "$name" in profile) if set_profile "$value"; then estep "Sélection du profil $(get_color g)$value$(get_color z)${SEARCHBASE:+ avec le dn de base $(get_color g)$SUFFIX$(get_color z)}" if [ -z "$LDAPURI" ]; then estep "Chargement des valeurs de $LDAPCONF" set_ldapuri "$(<"$LDAPCONF" grep '^URI' | sed 's/^URI *//g')" set_suffix "$(<"$LDAPCONF" grep '^BASE' | sed 's/^BASE *//g')" fi if [ -z "$LDAPURI" ]; then ewarn "Ce profil ne définit pas de serveur ldap, et aucun serveur n'a été défini dans $LDAPCONF" fi else eerror "Profil invalide ou erreur de connexion: $value" return 1 fi ;; *) if ! set_values "$varcmd"; then eerror "variable inconnue ou valeur invalide: $varcmd" return 1 fi ;; esac done else for name in "${VARIABLES[@]}"; do uname="$(awk '{print toupper($0)}' <<<"$name")" lname="$(awk '{print tolower($0)}' <<<"$name")" array_copy values "$uname" echo "$lname = ${values[*]}" done fi return 0 } function uldap_profile() { if [ -n "$*" ]; then uldap_set profile="$1" return $? else local profile profile_value profile_values for profile in "${PROFILES[@]}"; do echo_ "$profile" array_copy profile_values "${profile//-/_}_PROFILE" for profile_value in "${profile_values[@]}"; do if [ "${profile_value#ldapuri=}" != "$profile_value" ]; then echo_ $'\t'"${profile_value#ldapuri=}" elif [ "${profile_value#binddn=}" != "$profile_value" ]; then echo_ $'\t'"[${profile_value#binddn=}]" fi done echo done return 0 fi } function uldap_auth() { if [ -n "$*" ]; then set_values binddn="$1" ${2:+password="$2"} return 0 else local auth auth_value auth_values for auth in "${AUTH_PROFILES[@]}"; do echo_ "$auth" array_copy auth_values "${auth//-/_}_AUTH" for auth_value in "${auth_values[@]}"; do if [ "${auth_value#binddn=}" != "$auth_value" ]; then echo_ $'\t'"${auth_value#binddn=}" fi done echo done return 0 fi } function uldap_clear() { local args clear if parse_opts \ -k clear \ @ args -- "$@"; then set -- "${args[@]}" else eerror "$args" return 1 fi estep "Vidage de l'espace de travail" if [ -n "$clear" ]; then clear_wsfiles >"$WSFILE" else push_wsfile fi set_wsmode plain return 0 } function __after_load() { if [ "$WSMODE" == "ldif" ]; then is_yes "$UNCUT_ON_LOAD" && __transform --nopush uncut_lines is_yes "$DECODE_ON_LOAD" && __transform --nopush tl_decode "$(def_match_attr)" fi } function uldap_load() { # par défaut, on reset -u et -d, mais si on spécifie une des options, les honorer local args clear local -a options if parse_opts \ -k clear \ --nu '$array_add options --nu' \ --nc '$array_add options --nc' \ -u '$array_add options -u' \ --nd '$array_add options --nd' \ -d '$array_add options -d' \ @ args -- "$@"; then set -- "${args[@]}" else eerror "$args" return 1 fi [ -z "${options[*]}" ] && options=(-u -d) uldap_set "${options[@]}" local input="${1:-$OUTPUT}" if [ -f "$input" ]; then estep "Chargement depuis $input..." if [ -n "$clear" ]; then clear_wsfiles >"$WSFILE" elif [ -s "$WSFILE" ]; then push_wsfile is_yes "$WSAPPEND" && cat "$(peek_wsfile)" >"$WSFILE" fi set_values WSAPPEND=false cat "$input" >>"$WSFILE" || return set_values output="$input" if [ "${input%.ldif}" != "$input" ]; then set_wsmode ldif else set_wsmode plain fi __after_load __show_summary else eerror "$input: fichier introuvable" EXITCODE=1 return 1 fi EXITCODE=0 return 0 } function __before_save() { if [ "$WSMODE" == "ldif" ]; then is_yes "$ENCODE_ON_SAVE" && __transform --nopush tl_encode "$(def_match_attr "${ENCODE_ATTRS[@]}")" is_yes "$CUT_ON_SAVE" && __transform --nopush cut_lines fi } function uldap_save() { local args append if parse_opts \ -a append \ @ args -- "$@"; then set -- "${args[@]}" else eerror "$args" return 1 fi set_values output="${1:-$OUTPUT}" if [ -n "$OUTPUT" ]; then estep "Enregistrement de $OUTPUT..." __before_save if [ -n "$append" ]; then cat "$WSFILE" >>"$OUTPUT" || return else cat "$WSFILE" >"$OUTPUT" || return fi __after_load else eerror "Vous devez spécifier un fichier avec la commande save" return 1 fi return 0 } function __head() { # afficher le fichier $1 en le coupant à $LINES local file="$1" local nblines="$(<"$file" wc -l)" maxlines="${LINES:-24}" let maxlines=$maxlines-7 [ $maxlines -gt 1 ] || maxlines=5 head -n "$maxlines" <"$file" if [ $nblines -gt $maxlines ]; then eecho "$(get_color y)... $(($nblines - $maxlines)) lignes restantes$(get_color z)" fi } function __show_summary() { # Afficher un extrait de $wsfile en mode interactif [ -n "$INTERACTIVE" ] && __head "$WSFILE" } function uldap_print() { cat "$WSFILE" if [ -n "$INTERACTIVE" -a "$WSMODE" == "ldif" ]; then local nbdn="$(<"$WSFILE" grep '^dn:' | wc -l)" enote "$nbdn objet(s) trouvé(s)" fi return 0 } function uldap_alias() { local alias name searchbase local -a tmparray if [ -n "$*" ]; then for alias in "$@"; do if [[ "$alias" == *=* ]]; then name="${alias%%=*}" searchbase="${alias#$name=}" # supprimer l'ancien alias tmparray=() for alias in "${SEARCHBASE_ALIASES[@]}"; do if [ "${alias%%:*}" != "$name" ]; then array_add tmparray "$alias" fi done array_copy SEARCHBASE_ALIASES tmparray # ajouter le nouvel alias if [ -n "$searchbase" ]; then array_add SEARCHBASE_ALIASES "$name:$searchbase" fi else eerror "$alias: Vous devez utiliser la syntaxe 'alias name=searchbase'" fi done else for alias in "${SEARCHBASE_ALIASES[@]}"; do name="${alias%%:*}" searchbase="${alias#$name:}" echo "alias $name='$searchbase'" done fi return 0 } function uldap_cd() { local args exact if parse_opts \ -p exact \ @ args -- "$@"; then set -- "${args[@]}" else eerror "$args" return 1 fi local dn="$1" # corriger les expansions éventuelles du shell if [ "$dn" == "$HOME" ]; then dn="~" elif [ "${dn#$HOME}" != "$dn" ]; then dn="~${dn#$HOME}" fi # étendre les aliases local alias for alias in "${SEARCHBASE_ALIASES[@]}"; do name="${alias%%:*}" if [ "$dn" == "$name" ]; then # les aliases sont exprimés par rapport à $suffix dn="/$(absdn "${alias#$name:}")" break fi done if [ -z "$dn" -o "$dn" == "/" -o "$dn" == "~" -o "$dn" == ".." ]; then # arguments spéciaux : elif ! [[ "$dn" == *=* ]]; then # sinon, il faut que ce soit un rdn eerror "Alias ou dn invalide: $dn" return 1 fi [ -z "$dn" ] && dn="~" if [ -n "$exact" ]; then set_values searchbase_exact="$dn" elif [ "$dn" == ".." ]; then if [ "${SEARCHBASE#*,}" != "$SEARCHBASE" ]; then set_values searchbase_exact="${SEARCHBASE#*,}" else set_values searchbase_exact= fi else set_values searchbase_exact="$(rabsdn "$dn")" fi return 0 } function uldap_list() { if [ -z "$SEARCHBASE" -o \ \( "$SEARCHBASE" != "$SUFFIX" -a "${SEARCHBASE%,$SUFFIX}" == "$SEARCHBASE" \) ]; then uldap_ldapsearch "$@" else uldap_ldapsearch -s one "$@" fi } function uldap_ldapsearch() { if [ -z "$SEARCHBASE" -a -z "$*" ]; then set -- '' objectClass supportedLDAPVersion namingContexts fi local args nop cont debug deref tls scope local usearchbase lsearchbase="$SEARCHBASE" lfilter lattrs if parse_opts \ -n nop \ -c cont \ -d: debug= \ -s: scope= \ -a: deref= \ -Z tls \ -b: usearchbase= \ @ args -- "$@"; then set -- "${args[@]}" else eerror "$args" return 1 fi if [ -n "$usearchbase" ]; then lsearchbase="$(rabsdn "$usearchbase")" elif [ -z "$lsearchbase" -o \ \( "$lsearchbase" != "$SUFFIX" -a "${lsearchbase%,$SUFFIX}" == "$lsearchbase" \) ]; then # avec SEARCHBASE="" ou non relatif à $SUFFIX, le scope est par défaut base [ -n "$scope" ] || scope=base fi lfilter="${1:-${FILTER:-objectClass=*}}"; shift [[ "$lfilter" == \(*\) ]] || lfilter="($lfilter)" if [ -n "$*" ]; then lattrs=("$@") else lattrs=("${ATTRS[@]}") fi local -a lsargs=(ldapsearch -LLL -x ${LDAPURI:+-H "$LDAPURI"} ${tls:+-Z} ${nop:+-n} ${cont:+-c} ${debug:+-d "$debug"} ${scope:+-s "$scope"} ${deref:+-a "$deref"} -b "$lsearchbase") local msg="${lsargs[*]}" if [ -n "$BINDDN" ]; then lsargs=("${lsargs[@]}" -D "$BINDDN" -w "$PASSWORD") msg="$msg -D $BINDDN -w ****" fi estep "$msg $lfilter ${lattrs[*]}" if [ -s "$WSFILE" ]; then push_wsfile is_yes "$WSAPPEND" && cat "$(peek_wsfile)" >"$WSFILE" fi set_values WSAPPEND=false "${lsargs[@]}" "$lfilter" "${lattrs[@]}" >>"$WSFILE" EXITCODE=$? if [ $EXITCODE -eq 0 ]; then # ok seulement s'il y a un résultat [ -s "$WSFILE" ] && EXITCODE=0 || EXITCODE=1 else return 1 fi set_wsmode ldif __after_load __show_summary # affichage du nombre d'objets local nbdn="$(<"$WSFILE" grep '^dn:' | wc -l)" [ -n "$INTERACTIVE" ] && enote "$nbdn objet(s) trouvé(s)" return 0 } function __transform() { # appliquer la transformation $1 au fichier $WSFILE en utilisant $TMPFILE local push=1 if [ "$1" == "--nopush" ]; then shift push= fi cat "$WSFILE" >"$TMPFILE" [ -n "$push" ] && push_wsfile "$@" <"$TMPFILE" >"$WSFILE" } function __check_wsmode() { if [ "$WSMODE" == "mod" ]; then eerror "L'espace de travail est formatté pour ldapmodify. Vous ne devriez plus y appliquer de tranformation" return 1 fi return 0 } function uldap_transform() { local action="$1"; shift case "$action" in cut) __transform cut_lines ;; uncut) __transform uncut_lines ;; decode) __transform tl_decode "$(def_match_attr "$@")" ;; encode) __transform tl_encode "$(def_match_attr "$@")" set_values encode_attrs="$*" encode_on_save=false ;; addval) __check_wsmode || return __transform tl_addval "$@" __transform --nopush ensure_complete_objects ;; replval) __check_wsmode || return local match_attr="$(def_match_attr "$1")"; shift __transform tl_replval "$match_attr" "$@" __transform --nopush ensure_complete_objects ;; keepattr) __check_wsmode || return __transform tl_keepattr "$(def_match_attr dn "$@")" __transform --nopush ensure_complete_objects ;; keepval) __check_wsmode || return local match_attr="$(def_match_attr "$1")"; shift __transform tl_keepval "$match_attr" "$(def_match_value "$@")" __transform --nopush ensure_complete_objects ;; excludeattr) __check_wsmode || return __transform tl_excludeattr "$(def_match_attr "$@")" __transform --nopush ensure_complete_objects ;; excludeval) __check_wsmode || return local match_attr="$(def_match_attr "$1")"; shift __transform tl_excludeval "$match_attr" "$(def_match_value "$@")" __transform --nopush ensure_complete_objects ;; excludevalentry) __check_wsmode || return local match_attr="$(def_match_attr "$1")"; shift __transform tl_excludevalentry "$match_attr" "$(def_match_value "$@")" __transform --nopush delete_marked_objects ;; keepvalentry) __check_wsmode || return local match_attr="$(def_match_attr "$1")"; shift __transform tl_keepvalentry "$match_attr" "$(def_match_value "$@")" __transform --nopush delete_marked_objects ;; excludeempty) __check_wsmode || return __transform ensure_complete_objects ;; modaddattr) __check_wsmode || return __transform tl_addattr set_wsmode mod ;; modaddval) __check_wsmode || return __transform tl_modifyattr add set_wsmode mod ;; modreplval) __check_wsmode || return __transform tl_modifyattr replace set_wsmode mod ;; moddelval) __check_wsmode || return __transform tl_modifyattr delete set_wsmode mod ;; moddelattr) __check_wsmode || return __transform tl_deleteattr set_wsmode mod ;; moddelentry) __check_wsmode || return __transform tl_deleteentry set_wsmode mod ;; *) eerror "$action: type de transformation incorrect" return 1 ;; esac __show_summary return 0 } function uldap_sed() { __transform sed "$@" set_wsmode plain __show_summary return 0 } function uldap_awk() { __transform awk "$@" || return set_wsmode plain __show_summary return 0 } function uldap_grep() { __transform grep "$@" set_wsmode plain __show_summary return 0 } function uldap_format() { __transform tl_format "$@" set_wsmode plain __show_summary return 0 } function uldap_sort() { __transform sort "$@" set_wsmode plain __show_summary return 0 } function uldap_edit() { cat "$WSFILE" >"$TMPFILE" "${EDITOR:-vi}" "$TMPFILE" if testdiff "$WSFILE" "$TMPFILE"; then ac_set_tmpfile diff diff -U1 "$WSFILE" "$TMPFILE" | awk 'NR==1 { $2="previous" } NR==2 { $2="current" } {print}' >"$diff" __head "$diff" rm -f "$diff" push_wsfile cat "$TMPFILE" >"$WSFILE" else enote "Aucune modification effectuée" fi return 0 } function uldap_diff() { if has_wsfiles; then local orig="$(peek_wsfile)" if [ -n "$*" ]; then diff "$@" "$orig" "$WSFILE" else diff -U1 "$orig" "$WSFILE" | awk 'NR==1 { $2="previous" } NR==2 { $2="current" } {print}' fi else enote "Aucune différence détectée" fi return 0 } function uldap_ldapmodify() { if [ "$WSMODE" != "mod" ]; then ewarn "L'espace de travail n'a pas été traité avec l'une des méthodes de transformation A, a, r, d, D, delentry" if [ -n "$INTERACTIVE" ]; then ask_yesno "Voulez-vous néanmoins utiliser ldapmodify?" N || return fi fi local args nop cont debug verbose tls if parse_opts \ -n nop \ -c cont \ -d: debug= \ -v verbose \ -Z tls \ @ args -- "$@"; then set -- "${args[@]}" else eerror "$args" return 1 fi local -a lsargs=(ldapmodify -x ${LDAPURI:+-H "$LDAPURI"} ${tls:+-Z} ${nop:+-n} ${cont:+-c} ${debug:+-d "$debug"} ${verbose:+-v}) local msg="${lsargs[*]}" if [ -n "$BINDDN" ]; then lsargs=("${lsargs[@]}" -D "$BINDDN" -w "$PASSWORD") msg="$msg -D $BINDDN -w ****" fi estep "$msg" "${lsargs[@]}" -f "$WSFILE" EXITCODE=$? return $EXITCODE } function uldap_ldapadd() { local args nop cont debug verbose tls if parse_opts \ -n nop \ -c cont \ -d: debug= \ -v verbose \ -Z tls \ @ args -- "$@"; then set -- "${args[@]}" else eerror "$args" return 1 fi local -a lsargs=(ldapadd -x ${LDAPURI:+-H "$LDAPURI"} ${tls:+-Z} ${nop:+-n} ${cont:+-c} ${debug:+-d "$debug"} ${verbose:+-v}) local msg="${lsargs[*]}" if [ -n "$BINDDN" ]; then lsargs=("${lsargs[@]}" -D "$BINDDN" -w "$PASSWORD") msg="$msg -D $BINDDN -w ****" fi estep "$msg" "${lsargs[@]}" -f "$WSFILE" EXITCODE=$? return $EXITCODE } function uldap_ldapdelete() { local args nop cont debug verbose recursive tls if parse_opts \ -n nop \ -c cont \ -d: debug= \ -v verbose \ -r recursive \ -Z tls \ @ args -- "$@"; then set -- "${args[@]}" else eerror "$args" return 1 fi local -a lsargs=(ldapdelete -x ${LDAPURI:+-H "$LDAPURI"} ${tls:+-Z} ${nop:+-n} ${cont:+-c} ${debug:+-d "$debug"} ${verbose:+-v} ${recursive:+-r}) local msg="${lsargs[*]}" if [ -n "$BINDDN" ]; then lsargs=("${lsargs[@]}" -D "$BINDDN" -w "$PASSWORD") msg="$msg -D $BINDDN -w ****" fi if [ -n "$*" ]; then if [ -n "$INTERACTIVE" ]; then ewarn "Vous êtes sur le point du supprimer ${recursive:+DE FACON RECURSIVE }les objets suivants: $*" ask_yesno "Etes-vous sûr de vouloir continuer?" N || return estep "$msg $*" fi "${lsargs[@]}" "$@" EXITCODE=$? else if [ -n "$INTERACTIVE" ]; then __show_summary ewarn "Vous êtes sur le point de supprimer ${recursive:+DE FACON RECURSIVE }les DNs de l'espace de travail (un par ligne)" ask_yesno "Etes-vous sûr de vouloir continuer?" N || return estep "$msg" fi "${lsargs[@]}" -f "$WSFILE" EXITCODE=$? fi return $EXITCODE } function uldap_undo() { if has_wsfiles; then pop_wsfile __show_summary fi return 0 } function uldap_ifok() { if [ "$EXITCODE" -eq 0 ]; then eval_cmdline "$(qvals "$@")" else return 0 fi } function uldap_iferror() { if [ "$EXITCODE" -ne 0 ]; then eval_cmdline "$(qvals "$@")" else return 0 fi } function uldap_skip() { let SKIPCOUNT="${1:-1}" [ $SKIPCOUNT -eq 0 ] && SKIPCOUNT= return 0 } function uldap_quit() { exit "$EXITCODE" } ################################################################################ # Shell function display_help() { uecho "$scriptname: Shell pour accéder à un serveur ldap USAGE $scriptname [options] OPTIONS -C profile Sélectionner un profil de connexion. Par défaut, si l'option -H n'est pas spécifiée, le premier profil est sélectionné. -x Ne pas tenter de faire une connexion sur le profil par défaut si aucun profil n'est sélectionné. -f script Lire les commandes depuis le script spécifié. -n Avec un script donné en ligne de commande ou lu depuis un fichier, ne pas ajouter automatiquement la commande print à la fin -i Si un script est spécifié, passer en mode interactif après l'exécution du script. -e Forcer l'arrêt du script si une erreur se produit. C'est l'option par défaut pour un script spécifié avec -f. -l input.ldif Charger le fichier input.ldif comme espace de travail initial -H ldapuri -D binddn -w password -b searchbase -v var=value COMMANDES $COMMANDS_HELP" } PROFILES=(default) default_PROFILE=(filter="(objectClass=*)" attrs="") set_defaults uldap AUTOPROFILE=1 parse_opts + "${PRETTYOPTS[@]}" \ --help '$exit_with display_help' \ -C: lprofile \ -x AUTOPROFILE= \ -f: lscriptfile \ -n AUTOPRINT= \ -i INTERACTIVE=1 \ -e lexit_on_error \ -l: loutput \ -H: lldapuri \ -D: lbinddn \ -w: lpassword \ -b: lsearchbase \ -v: varcmds \ @ args -- "$@" && set -- "${args[@]}" || die "$args" [ -n "$lexit_on_error" ] && set_values exit_on_error=true # choix du profil if [ -n "$lprofile" ]; then # un profil spécifique if ! uldap_set profile="$lprofile"; then is_yes "$EXIT_ON_ERROR" && exit 255 fi elif [ -z "$lldapuri" -a -n "$AUTOPROFILE" ]; then # choisir le profil par défaut if ! uldap_set profile="${PROFILES[0]}"; then is_yes "$EXIT_ON_ERROR" && exit 255 fi fi if [ -n "$lldapuri" ]; then # augmenter le profil des paramètres de la ligne de commande if ! uldap_set ldapuri="$lldapuri" ${lbinddn:+binddn="$lbinddn"} ${lpassword:+password="$lpassword"}; then is_yes "$EXIT_ON_ERROR" && exit 255 fi fi [ -n "$lsearchbase" ] && set_values searchbase="$lsearchbase" # définitions de variables for varcmd in "${varcmds[@]}"; do set_values "$varcmd" || eerror "impossible de spécifier la valeur de ${varcmd%%=*}" done # script lcmds=() if [ -n "$*" ]; then [ "$INTERACTIVE" == auto ] && INTERACTIVE= || AUTOPRINT= INTERACTIVE_AFTER_CMDS="$INTERACTIVE" INTERACTIVE= [ "$EXIT_ON_ERROR" == auto ] && set_values exit_on_error=true # splitter sur les lignes array_from_lines lcmds "$(qvals "$@")" [ -n "$AUTOPRINT" ] && array_add lcmds print elif [ -n "$lscriptfile" ]; then [ -f "$lscriptfile" ] || die "$lscriptfile: fichier introuvable" [ "$INTERACTIVE" == auto ] && INTERACTIVE= || AUTOPRINT= INTERACTIVE_AFTER_CMDS="$INTERACTIVE" INTERACTIVE= [ "$EXIT_ON_ERROR" == auto ] && set_values exit_on_error=true # splitter sur les lignes array_from_lines lcmds "$(<"$lscriptfile" filter_comment -m)" [ -n "$AUTOPRINT" ] && array_add lcmds print elif [ "$INTERACTIVE" == auto ]; then isatty && INTERACTIVE=1 || INTERACTIVE= fi [ "$EXIT_ON_ERROR" == auto ] && set_values exit_on_error= function run_cmds() { # lancer toutes les commandes qui sont dans le tableau $1 local -a __rc_cmds local cmd array_copy __rc_cmds "${1:-cmds}" for cmd in "${__rc_cmds[@]}"; do # sauter éventuellement des commandes [ "$SKIPCOUNT" == "0" ] && SKIPCOUNT= if [ -n "$SKIPCOUNT" ]; then let SKIPCOUNT=$SKIPCOUNT-1 continue fi eval_cmdline "$cmd" done } # espace de travail initial [ -n "$loutput" ] && uldap_load "$loutput" # commandes non interactives if [ -n "${lcmds[*]}" ]; then run_cmds lcmds INTERACTIVE="$INTERACTIVE_AFTER_CMDS" fi # commandes interactives if [ -n "$INTERACTIVE" ]; then while true; do # construire le prompt prompt= tmp="$(reldn "$BINDDN")" prompt="$prompt${tmp:+[$tmp]}" prompt="$prompt$(get_color y)" if [ -n "$LDAPURI" ]; then split_ldapuri "$LDAPURI" proto host port || break if [ -n "$host" ]; then if [ "$proto" != "ldap" -a "$proto" != "ldaps" ]; then prompt="$prompt$proto://" fi prompt="$prompt$host" if [ -n "$port" -a "$port" != "389" ]; then prompt="$prompt:$port" fi else prompt="$prompt(default)" fi else prompt="${prompt}(default)" fi prompt="$prompt$(get_color z)" tmp="$(pdn "$SEARCHBASE")" [ -n "$FILTER" -o -n "$ATTRS" ] && tmp="$tmp?$ATTRS??$FILTER" prompt="$prompt${tmp:+ $tmp}" eecho "$prompt" # lire la commande prompt="$WSMODE" has_wsfiles && prompt="$prompt$(count_wsfiles)" read -p "$prompt> " ${EDITLAST:+-i "$LASTCMD"} -e cmds || break EDITLAST= [ -n "$cmds" ] || continue # lancer les commandes run_cmds cmds done echo fi exit "$EXITCODE"