Perl LDAP搜索 - 组中超过1500个成员

时间:2013-11-05 15:19:37

标签: perl active-directory ldap

我想使用Perl脚本和ldap连接搜索超过10,000名成员的组的所有成员。

我只能找到结果,如果我设置$first=0$last=1499而我只获得该组的前1500名成员。 如果我对$first$last使用其他参数,那么我没有结果。

"$ldapsearchresult = $ldapconnect->search (
Sizelimit => 0,
base   => 'any_base',
filter => '(objectClass=*)',
attr => ['member;range=$first-$last'],
);"

感谢您的帮助!

3 个答案:

答案 0 :(得分:1)

您需要一次又一次地搜索属性范围作为子类型,直到最后一次返回'*'。

这是我正在使用的代码,也是在AD中使用分页搜索。

use Net::LDAP;
use Net::LDAP qw(LDAP_CONTROL_PAGED);
use Net::LDAP::Util qw(ldap_error_name canonical_dn ldap_explode_dn ldap_error_text);
use Net::LDAP::Control::Paged;

my $page_page = Net::LDAP::Control::Paged->new( 'size' => $input{'page'} );
my $finished_search = 0;
my $page_cookie;
my $result;
my @page_search_args = (
    'base'     => $input{"base"},               
    'scope'    => $input{'scope'},                             
    'filter'   => $input{'filter'},  
    'attrs'    => $input{'attrs'},  
    'control'  => [ $page_page ],
    'deref'    => 'never',
    'raw'      => qr!^DO_NOT_MATCH!,
);

while (!$finished_search) {
        my $msg = $ldap->search(@page_search_args);
        if ($msg->is_error()) {
            die "ERROR: ",$msg->error,"\n";
            last;
        } else {
            my ($response) = $msg->control(LDAP_CONTROL_PAGED);
            $page_cookie = $response->cookie();
            $finished_search = 1 if !$page_cookie;
            $page_page->cookie($page_cookie);

            while (my $entry = $msg->pop_entry()){
                $ldap_searches++;
                print_all_attributes($entry);
            }
        }
}

if ($page_cookie) {
        $page_page->cookie($page_cookie);
        $page_page->size(0);
        $ldap->search(@page_search_args);
}   

sub add_result {
    my $dn     = shift;
    my $attr   = shift;
    my $data   = shift;
    my $res    = shift; 

    $attr =~ s!(;range\=\d+\-\d+)!!i;
    #print "removed $1 from $attr" if $1;
    foreach my $subtype (keys %{$data}){
        $attr = $attr.$subtype if $subtype ne '';
        $attr =~ s!(;range\=\d+\-\d+)!!i;
        if (defined $$res->{$dn}->{$attr}){
            push(@{$$res->{$dn}->{$attr}},@{$data->{$subtype}});
        } else {
            push(@{$$res->{$dn}->{$attr}},@{$data->{$subtype}});
        }
    }

    return $res;    
}
sub print_all_attributes {
    my $entry = shift;
    foreach my $attr ($entry->attributes()) {
        if ($attr =~ /;range=/) {   
            my $last = 0;my $first = 0;
            ### $var will look like this --> "member;range=0-1499"
            (my $pure_attr,my $range) = split /;/, $attr,2;
            (my $junk,$range)         = split /=/, $range,2;
            ($first,$last)            = split /-/, $range,2;
            $i++;
            add_result($entry->dn(),$pure_attr,$entry->get_value($attr,alloptions => 1, asref => 1),\$result) if $last eq '*' or $last >= $parms{'attribute_page'}; 
            ### if $last eq "*", indicates this is the last range increment, and
            ### we do not need to perform another supplemental search
            if ($last ne "*") {                                     
                my $range_diff = ($last - $first) + 1;
                my $increment =  $last + $range_diff;
                $last = $last + 1;
                $attr = "$pure_attr;range=$last-$increment";
                $parms{'attrs'} = [$attr];
                search_nonpaged(%parms);            
            }       
        } else {### if $attr matches range pattern                  
            add_result($entry->dn(),$attr,$entry->get_value($attr,alloptions => 1, asref => 1),\$result);
        }
    }
    return 1;
}
sub search_nonpaged{
    my %input = @_;
    my @page_search_args = (
        'base'     => $input{"base"},               
        'scope'    => $input{'scope'},                             
        'filter'   => $input{'filter'},  
        'attrs'    => $input{'attrs'},  
        'deref'    => 'never',
        'raw'      => qr!^DO_NOT_MATCH!,
    );  
    my $msg = $ldap->search(@page_search_args);
    if ($msg->is_error()) {
        die "ERROR: ",$msg->error,"\n"; 
    }
    while (my $entry = $msg->pop_entry()){
        $ldap_searches++;
        print_all_attributes($entry);
    }
}

答案 1 :(得分:0)

您可以通过搜索来简化程序: memberOf = CN = GroupOne,OU =安全组,OU =组,DC = YOURDOMAIN,DC = NET

您仍然需要使用分页结果控件,但不需要范围控制。

Microsoft Active Directory使用MaxValRange来控制在检索条目的多值属性时返回的值的数量。

通过使用上面的过滤器,您可以避免MaxValRange设置。

按方式:如果您还想获得嵌套成员,请尝试:

(memberOf:1.2.840.113556.1.4.1941:= CN = GroupOne,OU =安全组,OU =组,DC = YOURDOMAIN,DC = NET)

此过滤器使用LDAP_MATCHING_RULE_IN_CHAIN可扩展匹配。

-Jim

答案 2 :(得分:0)

我找到了一种更简单的方法来搜索AD组的所有成员:

http://permalink.gmane.org/gmane.comp.lang.perl.modules.ldap/246

use Net::LDAP; use Net::LDAP::Util;

# Connect to AD make sure to specify version 3

$ldap = new Net::LDAP("myGC.yy.xx.com",
                      port => 3268,
                      debug => 0,
                      version => 3
    ) or die "New failed:$ <at> ";

# Do an anonymous bind. You MAY have to do an authenticated bind in your configuration

$result=$ldap->ldapbind() || die "Bind Failed:$ <at> ";

# Some error trapping

$err=$result->code;

if ($err){
    $errname=Net::LDAP::Util::ldap_error_name($err);
    $errtxt=Net::LDAP::Util::ldap_error_text($err);
    if ($errtxt){
        print "($err) $errtxt\n";
    }
    else
    {
        if ($errname){
            print "($err) $errname\n";
        }
        else
        {
            print "ERR: $err\n";
        }
    }
    exit;
}

# The combination of the search base and filter determine which object that you
# retrieve

# set search filter to groups of objects. This is what you want to enumerate NT groups.

$filter="(objectClass=group)";

# Set the search base to the DN of the object that you want to retrieve. BTW, using this method on
# groups with less than 1000 members works as well.

$base='CN=mygroup,DCyyy,DC=xxx,DC=com';

# Set the initial attribute indexes and name

$found=1; $startr=0; $endr=-1; $startattr="member";

while($found){

    # Create the attribute range specification

    $startr=$endr+1;
    $endr=$startr+999;
    $attr="$startattr;range=$startr-$endr";
    $saveattr=$attr;
    <at> attr=("$attr");

    # Perform the search

    $result=$mesg = $ldap->search(base   => "$base",filter => $filter,
                                  attrs => [ <at> attr],
                                  scope => "sub") or die "search died";

    # Some error trapping

    $err=$result->code;
    if ($err){
        if (!($err == 1)){
            $errname=Net::LDAP::Util::ldap_error_name($err);
            $errtxt=Net::LDAP::Util::ldap_error_text($err);
            if ($errtxt){
                print "($err) $errtxt\n";
            }
            else
            {
                if ($errname){
                    print "($err) $errname\n";
                }
                else
                {
                    print "ERR: $err\n";
                }
            }
        }
        else
        {
            print "COUNT=$cnt\n";
        }
        exit;
    }

    $found=0;

    # OK, get the attribute range...so we can update the value of the attribute
    # on the next pass

    foreach $entry ($mesg->all_entries) {
        <at> attr=$entry->attributes;
        foreach( <at> attr){
            $curattr=$_;
        }
    }

    # Print out the current chunk of members

    foreach $entry ($mesg->all_entries) {
        $ar=$entry->get("$curattr");
        foreach( <at> $ar){
            $cnt++;
            print "$_\n";
        }
        $found=1;
        if (! <at> $ar[0]){
            $found=0;
        }
    }

    # Check to see if we got the last chunk. If we did print toe total and set
    # the found flag so we don't search for anymore members

    if ($curattr=~/\;range=/){
        if ($curattr=~/\-\*/){
            print "LASTCOUNT:$cnt\n";
            $found=0;
        }
    }

}