Please support nested groups
- Dominant language
- Python
- Stars
- 156
- Forks
- 62
- Avg merge
- 1m
- Merged PRs (30d)
- 4
Description
Currently the code seems to assume that the value of a `member` attribute is the DN of a user, and treats the RDN as the uid (even if it happens to be called `cn` and `ldap_uidattr` is unset, and even if the LDAP member entity doesn't have the posixAccount objectClass, which I'd say is a bug).
Under rfc2307bis, group members can also be groups; libnss-ldap also supports this.
I think `nsscache` should perform a transitive closure on the groups it obtains from LDAP. I wrote a shell script that does this, as a proof of concept; it's a bit crude, but it works. It allows arbitrary group nesting (even across groups that don't have the `posixGroup` objectClass). It doesn't prevent infinite recursion though, and it's very slow.
Ironically, `getent group` gets it right if nss is configured to use libnss-ldap, so that instead of running `nsscache`, we could just use `getent` -- if that wouldn't defeat the purpose of using `nss-cache`. :)
``` zsh
#!/bin/zsh
#
# Purpose: get groups from LDAP recursively and extract their members.
typeset -A isgroup
typeset -A isposixgroup
typeset -A members # Contains DNs
typeset -A memberuids
typeset -A processed_DNs
typeset -A uids
typeset -A groups
typeset -A memberfilter
GROUPBASE="$1"
GROUPFILTER="$2" # only print groups matching this filter; use objectClass=posixGroup
MEMBERFILTER="$3" # only print members matching this filter; use objectClass=posixAccount
function process_ldapsearch() {
local currentdn=""
local memberdn=""
local memberuid=""
while read attrib val; do case "$attrib $val" in
dn:*)
currentdn="$val"
;;
objectClass:\ groupOfNames)
isgroup[$currentdn]=1
;;
objectClass:\ posixGroup)
isposixgroup[$currentdn]=1
;;
member:\ *)
memberdn="$val"
# Do we already have this member in the current group? If not, add it.
[[ "$members[$currentdn]" =~ \b${memberdn}\b ]] || members[$currentdn]="$members[$currentdn] $memberdn"
[[ "${memberdn/,$GROUPBASE/}" = "${memberdn}" ]] && # Is this member located directly under our specified GROUPBASE dn?
[[ "${memberdn/uid=/}" = "${memberdn}" ]] && { # Process members that are outside our search base and that are not obviously users
[[ $processed_DNs[$memberdn] = "" ]] && ldapsearch -LLL -x -o ldif-wrap=no -b "${memberdn}" | process_ldapsearch
processed_DNs[$memberdn]=1
}
;;
memberUid:*)
memberuid="$val"
# Do we already have this member in the current group? If not, add it.
[[ "$memberuids[$currentdn]" =~ \b$memberuid\b ]] || memberuids[$currentdn]="$memberuids[$currentdn] $memberuid"
;;
*)
;;
esac; done
}
function memberfilter_match() {
[[ -z "$MEMBERFILTER" ]] && return 0
[[ -n "$memberfilter[$1]" ]] && return $memberfilter[$1]
if [[ $(ldapsearch -s base -LLL -x -o ldif-wrap=no -b "$i" "($MEMBERFILTER)" dn) = "dn: $i" ]]; then
memberfilter[$1]="0"
else
memberfilter[$1]="1"
fi
return $memberfilter[$1]
}
function getmembers() {
local group=$1
local -A curmembers
local i
local j
for i in ${(z)members[$group]}; do
if (( ${+isgroup[$i]} || ${+isposixgroup[$i]} )); then
for j in $(getmembers $i); do
curmembers[$j]=1
done
else
if memberfilter_match $i; then
i=${i/uid=/}
i=${i/,*/}
curmembers[$i]=1
fi
fi
done
for i in ${(z)memberuids[$group]}; do
curmembers[$i]=1
done
echo ${(k)curmembers}
return 0;
}
# main()
ldapsearch -LLL -x -b "$GROUPBASE" -o ldif-wrap=no | process_ldapsearch
# Merge the list of posixGroups and groupOfNames groups; the keys of the groups hash will form a list of all groups
for group in ${(k)isposixgroup} ${(k)isgroup}; do
groups[$group]=1
done
for group in ${(k)groups}; do
if [[ -z "$GROUPFILTER" ]] || [[ $(ldapsearch -s base -LLL -x -b "$group" -o ldif-wrap=no "($GROUPFILTER)" dn) = "dn: $group" ]] then
groupname=${group/cn=/}
if ! [[ "${groupname/,$GROUPBASE/}" = "${groupname}" ]]; then # Was this particular group inside our search base or outside it?
groupname=${groupname/,*/}
echo $groupname $(getmembers $group | tr ' ' '\n' | sort | tr '\n' ' ')
fi
fi
done
```
Contributor guide
Assessment
This issue has not been assessed yet.