header merge from Teckids assoldap; remaining changes to follow
[shellsnippets/shellsnippets.git] / mksh / assoldap.ksh
1 # -*- mode: sh -*-
2 #-
3 # Copyright © 2013
4 #       mirabilos <t.glaser@tarent.de>
5 # Copyright © 2014, 2015
6 #       Dominik George <dominik.george@teckids.org>
7 # Copyright © 2014, 2015
8 #       mirabilos <thorsten.glaser@teckids.org>
9 #
10 # Provided that these terms and disclaimer and all copyright notices
11 # are retained or reproduced in an accompanying document, permission
12 # is granted to deal in this work without restriction, including un‐
13 # limited rights to use, publicly perform, distribute, sell, modify,
14 # merge, give away, or sublicence.
15 #
16 # This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
17 # the utmost extent permitted by applicable law, neither express nor
18 # implied; without malicious intent or gross negligence. In no event
19 # may a licensor, author or contributor be held liable for indirect,
20 # direct, other damage, loss, or other issues arising in any way out
21 # of dealing in the work, even if advised of the possibility of such
22 # damage or existence of a defect, except proven that it results out
23 # of said person’s immediate fault when using the work as intended.
24 #-
25 # Generic LDAP (LDIF) parser into associative arrays.
26
27 # include assockit and pure-mksh base64 decoder, unless already done
28 mydir=$(realpath "$(dirname "$0")")
29 [[ -n $ASSO_VAL ]] || PATH="$mydir:$mydir/..:$PATH" . assockit.ksh
30 typeset -f Lb64decode >/dev/null || PATH="$mydir:$mydir/..:$PATH" . base64
31
32 # Syntax: asso_setldap arrayname index ... -- ldapsearch-options
33 function asso_setldap {
34         # parse options
35         local arrpath ldapopts x i=0 T dn line value
36         set -A arrpath
37         while (( $# )); do
38                 [[ $1 = -- ]] && break
39                 arrpath[i++]=$1
40                 shift
41         done
42         if [[ $1 != -- ]]; then
43                 print -u2 'assoldap.ksh: syntax: asso_setldap arraypath -- ldappath'
44                 return 255
45         fi
46         shift
47         set -A ldapopts -- "$@"
48
49         # just in case, unset the target array and create it as associative
50         asso__lookup 1 "${arrpath[@]}"
51         asso__r_free
52         asso__r_setf $ASSO_AASS
53
54         # call ldapsearch with decent output format
55         if ! T=$(mktemp -d /tmp/assoldap.XXXXXXXXXX); then
56                 print -u2 'assoldap.ksh: could not create temporary directory'
57                 return 255
58         fi
59         (ldapsearch -xLLL "${ldapopts[@]}"; echo $? >"$T/err") | \
60             tr '\n' $'\a' | sed -e $'s/\a //g' >"$T/out"
61         i=$(<"$T/err")
62         if (( i )); then
63                 print -u2 'assoldap.ksh: ldapsearch returned error'
64                 rm -rf "$T"
65                 return $i
66         fi
67         if [[ ! -s $T/out ]]; then
68                 # empty output
69                 rm -rf "$T"
70                 return 0
71         fi
72
73         # parse LDIF (without linewraps)
74         while IFS= read -d $'\a' -r line; do
75                 if [[ -z $line ]]; then
76                         dn=
77                         continue
78                 fi
79                 value=${line##+([!:]):?(:)*( )}
80                 if [[ $value = "$line" ]]; then
81                         print -ru2 "assoldap.ksh: malformed line: $line"
82                         rm -rf "$T"
83                         return 255
84                 fi
85                 x=${line%%*( )"$value"}
86                 if [[ $x = "$line" ]]; then
87                         print -ru2 "assoldap.ksh: malformed line: $line"
88                         rm -rf "$T"
89                         return 255
90                 fi
91                 [[ $x = *:: ]] && value=$(Lb64decode "$value")
92                 x=${x%%+(:)}
93                 if [[ -z $dn ]]; then
94                         if [[ $x = dn ]]; then
95                                 dn=$value
96                         else
97                                 print -ru2 "assoldap.ksh: not dn: $line"
98                                 rm -rf "$T"
99                                 return 255
100                         fi
101                 elif [[ $x = dn ]]; then
102                         print -ru2 "assoldap.ksh: unexpected dn ($dn): $line"
103                         rm -rf "$T"
104                         return 255
105                 fi
106                 asso_sets "$value" "${arrpath[@]}" "$dn" "$x"
107         done <"$T/out"
108         rm -rf "$T"
109         if [[ -n $dn ]]; then
110                 print -u2 'assoldap.ksh: missing empty line at EOT'
111                 return 255
112         fi
113         return 0
114 }
115
116 :||\
117 {
118         # for testing
119         LDAPTLS_CACERT=/etc/ssl/certs/dc.lan.tarent.de.cer \
120             asso_setldap users -- \
121             -H ldaps://dc.lan.tarent.de -b cn=users,dc=tarent,dc=de -s one \
122             isJabberAccount=1 cn uid
123         if (( $? )); then
124                 print -u2 An error occurred: $?
125                 exit 1
126         fi
127         print "uid (dn) = cn"
128         asso_loadk users
129         for user_dn in "${asso_y[@]}"; do
130                 print -r -- "$(asso_getv users "$user_dn" uid)" \
131                     "($user_dn) = $(asso_getv users "$user_dn" cn)"
132         done | sort
133 }