#!/usr/bin/perl -w # # Adfinis AG # # Flatten nested LDAP groups # # Workaround to corcumvent Keycloaks special logic to handle # nested groups - it assume they are built up in a tree, each # child groups only has a single parent. # # So we walk through all groups and replace references to other # groups by its members. The process is recursively, there is no # lmit how deep the nesting becomes. So be careful not to create # infinite loops :-) # # We assume: # * rhe input LDIF doesn't have line breaks # * all groups are of kind groupOfUniqueNames and posixGroup # use strict; use Data::Dumper; my $in="concat.ldif"; my $out="target.ldif"; my $g; my $u; &readusers; &readgroups; while (&denest) {} #print Dumper $g; &combine; sub readusers { my $isuser=0; my $dn; my $uid; open IN, $in; while() { chomp; if (/^objectClass: inetOrgPerson/) { $isuser=1; } elsif (/^dn: (.*)/) { $dn=$1; } elsif (/^uid: (.*)/) { $uid=$1; } elsif (/^$/) { if ($isuser) { $u->{$dn}=$uid; } $isuser=0; undef $dn; undef $uid; } } close IN; } sub readgroups { my $isgroup=0; my $dn; my @members; open IN, $in; while() { chomp; if (/^objectClass: groupOfUniqueNames/) { $isgroup=1; } elsif (/^dn: (.*)/) { $dn=$1; } elsif (/^uniqueMember: (.*)/) { push @members, $1; } elsif (/^$/) { if ($isgroup) { foreach my $member (@members) { $g->{$dn}->{$member}=$u->{$member}; } } $isgroup=0; undef $dn; undef @members; } } close IN; } sub denest { my @members; my $ret=0; foreach my $dn (keys %{$g}) { foreach my $member (keys %{$g->{$dn}}) { if (defined $g->{$member}) { foreach my $submember (keys %{$g->{$member}}) { push @members, $submember; } $ret=1; } else { push @members, $member; } } undef $g->{$dn}; foreach my $member (@members) { $g->{$dn}->{$member}=$u->{$member}; } undef @members; } return $ret; } sub combine { my $dn; open IN, $in; open OUT, ">$out"; while() { chomp; if (/^dn: (.*)/) { $dn=$1; print OUT "$_\n"; } elsif (/^uniqueMember: .*/) { } elsif (/^memberUid: .*/) { } elsif (/^$/){ if (defined $g->{$dn}) { foreach my $member (keys %{$g->{$dn}}) { print OUT "uniqueMember: $member\n"; } foreach my $member (keys %{$g->{$dn}}) { if (defined $u->{$member}) { print OUT "memberUid: $u->{$member}\n"; } } } print OUT "\n"; undef $dn; } else { print OUT "$_\n"; } } close IN; close OUT; }