From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 25FD96136F for ; Tue, 25 Jul 2023 15:13:58 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id F1BBE1A8D8 for ; Tue, 25 Jul 2023 15:13:27 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Tue, 25 Jul 2023 15:13:26 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 89354434C0 for ; Tue, 25 Jul 2023 15:13:26 +0200 (CEST) From: Christoph Heiss To: pve-devel@lists.proxmox.com Date: Tue, 25 Jul 2023 15:12:55 +0200 Message-ID: <20230725131321.1137607-1-c.heiss@proxmox.com> X-Mailer: git-send-email 2.41.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.050 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record T_SCC_BODY_TEXT_LINE -0.01 - URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [ldap.pm] Subject: [pve-devel] [PATCH common] ldap: de-duplicate LDAP search for users and groups X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 25 Jul 2023 13:13:58 -0000 For the most part, query_users() and query_groups() are identical, so extract the common part into a separate method and let query_{users,groups}() be simple wrappers over it. No change in functionality. Signed-off-by: Christoph Heiss --- src/PVE/LDAP.pm | 192 ++++++++++++++++++++---------------------------- 1 file changed, 81 insertions(+), 111 deletions(-) diff --git a/src/PVE/LDAP.pm b/src/PVE/LDAP.pm index 342c352..a56b957 100644 --- a/src/PVE/LDAP.pm +++ b/src/PVE/LDAP.pm @@ -99,29 +99,41 @@ sub auth_user_dn { return 1; } -sub query_users { - my ($ldap, $filter, $attributes, $base_dn, $classes) = @_; +my $build_filter = sub { + my ($expr, $parts) = @_; - # build filter from given filter and attribute list - my $tmp = "(|"; - foreach my $att (@$attributes) { - $tmp .= "($att=*)"; + my $res = '(|'; + for my $part (@$parts) { + $res .= &$expr($part); } - $tmp .= ")"; - if ($classes) { - $tmp = "(&$tmp(|"; - for my $class (@$classes) { - $tmp .= "(objectclass=$class)"; - } - $tmp .= "))"; - } + return "$res)"; +}; - if ($filter) { - $filter = "($filter)" if $filter !~ m/^\(.*\)$/; - $filter = "(&${filter}${tmp})" +my $query_objects = sub { + my ($ldap, $base_dn, $custom_filter, $classes, $attributes, $search_attrs) = @_; + + # build filter from given class and attribute list + my $class_filter = &$build_filter(sub { "(objectclass=$_[0])" }, $classes) if defined($classes); + my $attr_filter = &$build_filter(sub { "($_[0]=*)" }, $attributes) if defined($attributes); + + # $class_filter is always defined + my $filter; + if (defined($class_filter) && defined($attr_filter)) { + $filter = "(&${class_filter}${attr_filter})"; } else { - $filter = $tmp; + $filter = $class_filter || $attr_filter; + } + + # combine custom (user-supplied) filter and internal one using AND + if ($custom_filter) { + $custom_filter = "($custom_filter)" if $custom_filter !~ m/^\(.*\)$/; + + if ($filter) { + $filter = "(& {${custom_filter}${filter})"; + } else { + $filter = $custom_filter; + } } my $page = Net::LDAP::Control::Paged->new(size => 900); @@ -131,40 +143,23 @@ sub query_users { scope => "subtree", filter => $filter, control => [ $page ], - attrs => [ @$attributes, 'memberOf'], + attrs => $search_attrs, ); my $cookie; my $err; - my $users = []; + my $objs = []; while(1) { - my $mesg = $ldap->search(@args); # stop on error if ($mesg->code) { - $err = "ldap user search error: " . $mesg->error; + $err = $mesg->error; last; } - #foreach my $entry ($mesg->entries) { $entry->dump; } - foreach my $entry ($mesg->entries) { - my $user = { - dn => $entry->dn, - attributes => {}, - groups => [$entry->get_value('memberOf')], - }; - - foreach my $attr (@$attributes) { - my $vals = [$entry->get_value($attr)]; - if (scalar(@$vals)) { - $user->{attributes}->{$attr} = $vals; - } - } - - push @$users, $user; - } + push @$objs, $mesg->entries; # Get cookie from paged control my ($resp) = $mesg->control(LDAP_CONTROL_PAGED) or last; @@ -181,93 +176,68 @@ sub query_users { $page->cookie($cookie); $page->size(0); $ldap->search(@args); - $err = "LDAP user query unsuccessful" if !$err; + $err = "query unsuccessful" if !$err; } - die $err if $err; + die "$err\n" if $err; + return $objs; +}; - return $users; -} - -sub query_groups { - my ($ldap, $base_dn, $classes, $filter, $group_name_attr) = @_; +sub query_users { + my ($ldap, $filter, $attributes, $base_dn, $classes) = @_; - my $tmp = "(|"; - for my $class (@$classes) { - $tmp .= "(objectclass=$class)"; - } - $tmp .= ")"; + my $search_attrs = [ @$attributes, 'memberOf' ]; + my $objs = &$query_objects($ldap, $base_dn, $filter, $classes, $attributes, $search_attrs); + + my @users = map { + my $entry = $_; + my $user = { + dn => $entry->dn, + attributes => {}, + groups => [$entry->get_value('memberOf')], + }; + + foreach my $attr (@$attributes) { + my $vals = [$entry->get_value($attr)]; + if (scalar(@$vals)) { + $user->{attributes}->{$attr} = $vals; + } + } - if ($filter) { - $filter = "($filter)" if $filter !~ m/^\(.*\)$/; - $filter = "(&${filter}${tmp})" - } else { - $filter = $tmp; - } + $user + } @$objs; - my $page = Net::LDAP::Control::Paged->new(size => 100); + return \@users; +} - my $attrs = [ 'member', 'uniqueMember' ]; - push @$attrs, $group_name_attr if $group_name_attr; - my @args = ( - base => $base_dn, - scope => "subtree", - filter => $filter, - control => [ $page ], - attrs => $attrs, - ); +sub query_groups { + my ($ldap, $base_dn, $classes, $filter, $group_name_attr) = @_; - my $cookie; - my $err; - my $groups = []; + my $search_attrs = [ 'member', 'uniqueMember' ]; + push @$search_attrs, $group_name_attr if $group_name_attr; - while(1) { + my $objs = &$query_objects($ldap, $base_dn, $filter, $classes, undef, $search_attrs); - my $mesg = $ldap->search(@args); + my @groups = map { + my $entry = $_; + my $group = { + dn => $entry->dn, + members => [], + }; - # stop on error - if ($mesg->code) { - $err = "ldap group search error: " . $mesg->error; - last; + my $members = [$entry->get_value('member')]; + if (!scalar(@$members)) { + $members = [$entry->get_value('uniqueMember')]; } - - foreach my $entry ( $mesg->entries ) { - my $group = { - dn => $entry->dn, - members => [] - }; - my $members = [$entry->get_value('member')]; - if (!scalar(@$members)) { - $members = [$entry->get_value('uniqueMember')]; - } - $group->{members} = $members; - if ($group_name_attr && (my $name = $entry->get_value($group_name_attr))) { - $group->{name} = $name; - } - push @$groups, $group; + $group->{members} = $members; + if ($group_name_attr && (my $name = $entry->get_value($group_name_attr))) { + $group->{name} = $name; } - # Get cookie from paged control - my ($resp) = $mesg->control(LDAP_CONTROL_PAGED) or last; - $cookie = $resp->cookie; - - last if (!defined($cookie) || !length($cookie)); - - # Set cookie in paged control - $page->cookie($cookie); - } - - if ($cookie) { - # We had an abnormal exit, so let the server know we do not want any more - $page->cookie($cookie); - $page->size(0); - $ldap->search(@args); - $err = "LDAP group query unsuccessful" if !$err; - } - - die $err if $err; + $group + } @$objs; - return $groups; + return \@groups; } 1; -- 2.41.0