超出了PHP ldap_search大小限制

时间:2011-12-26 13:53:37

标签: php search ldap size limit

我很难查询微软的Active Directory并遇到一些困难:

每个请求的AD大小限制为1000个元素。我不能改变大小限制。 PHP似乎不支持分页(我使用的是5.2版,而且无法更新生产服务器。)

到目前为止,我遇到了两种可能的解决方案:

  1. 按objectSid对条目进行排序,并使用过滤器获取所有对象。 Sample Code
    我不喜欢这个有几个原因:
    • 混淆objectSid似乎无法预测,因为你必须将它拆开,将其转换为十进制,然后将其转换回来......
    • 我看不出你如何比较这些id (我试过了:'&((objectClass = user)(objectSid> = 0))')

  2. 在对象名称的第一个字母后过滤(如建议here):
    这不是最佳解决方案,因为我们系统中的许多用户/组都使用相同的几个字母作为前缀。
  3. 所以我的问题:

    这里最好使用哪种方法?
    如果是第一个,我怎样才能确保正确处理objectSid?

    还有其他可能吗? 我错过了一些明显的东西吗?

    更新
    - This相关问题提供有关简单分页结果扩展无效的原因的信息 - Web服务器在Linux服务器上运行,因此COM对象/ adoDB不是一个选项。

4 个答案:

答案 0 :(得分:1)

永远不要对服务器或服务器配置做出假设,这会导致代码脆弱,出现意外,有时甚至是惊人的失败。仅仅因为它今天是AD并不意味着它将是明天,或者微软不会改变服务器中的默认限制。我最近处理的情况是客户端代码是用部落知识编写的,大小限制是2000,当管理员出于自己的原因改变了大小限制时,客户端代码失败了。

您确定PHP不支持请求控件(简单的分页结果扩展是请求控件)吗?我写了一篇关于"LDAP: Simple Paged Results"的文章,虽然文章示例代码是Java,但概念很重要,而不是语言。另请参阅"LDAP: Programming Practices"

答案 1 :(得分:1)

由于我没有找到任何干净的解决方案,我决定采用第一种方法:按对象方式过滤。

这种解决方法有其局限性:

  1. 它仅适用于具有objectsid的对象,即用户和组。
  2. 它假定所有用户/组都由同一权限创建。
  3. 假设缺少相对SID而不是大小限制。
  4. 这个想法是首先读取所有可能的对象并选择具有最低相对SID的对象。相对SID是SID中的最后一个块:

    S-1-5-21-3188256696-111411151-3922474875- 1158

    假设这是仅返回“部分搜索结果”的搜索中的最低相对SID。 让我们进一步假设大小限制是1000。

    该程序然后执行以下操作: 它使用

    之间的SID搜索所有对象

    S-1-5-21-3188256696-111411151-3922474875-的 1158

    S-1-5-21-3188256696-111411151-3922474875- 0159

    然后全部在

    之间

    S-1-5-21-3188256696-111411151-3922474875-的 1158

    S-1-5-21-3188256696-111411151-3922474875-的 2157

    等等,直到其中一个搜索返回零对象。

    这种方法存在一些问题,但这对我的目的来说已经足够了 守则:

    $filter = '(objectClass=Group)';
    $attributes = array('objectsid','cn'); //objectsid needs to be set
    
    $result = array();
    
    $maxPageSize = 1000;
    $searchStep = $maxPageSize-1;
    
    $adResult = @$adConn->search($filter,$attributes); //Supress warning for first query (because it exceeds the size limit)
    
    //Read smallest RID from the resultset
    $minGroupRID = '';
    
    for($i=0;$i<$adResult['count'];$i++){
        $groupRID = unpack('V',substr($adResult[$i]['objectsid'][0],24));
        if($minGroupRID == '' || $minGroupRID>$groupRID[1]){
            $minGroupRID = $groupRID[1];
        }    
    }
    
    $sidPrefix =  substr($adResult[$i-1]['objectsid'][0],0,24);   //Read last objectsid and cut off the prefix
    
    $nextStepGroupRID = $minGroupRID;
    
    do{ //Search for all objects with a lower objectsid than minGroupRID
        $adResult = $adConn->search('(&'.$filter.'(objectsid<='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID))).')(objectsid>='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID-$searchStep))).'))', $attributes);
    
        for($i=0;$i<$adResult['count'];$i++){
            $RID = unpack('V',substr($adResult[$i]['objectsid'][0],24));    //Extract the relative SID from the SID
            $RIDs[] = $RID[1];
    
            $resultSet = array();
            foreach($attributes as $attribute){
                $resultSet[$attribute] = $adResult[$i][$attribute][0];
            }
            $result[$RID[1]] = $resultSet;
        }
    
        $nextStepGroupRID = $nextStepGroupRID-$searchStep;
    
    }while($adResult['count']>1);
    
    $nextStepGroupRID = $minGroupRID;
    
    do{ //Search for all object with a higher objectsid than minGroupRID  
        $adResult = $adConn->search('(&'.$filter.'(objectsid>='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID))).')(objectsid<='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID+$searchStep))).'))', $attributes);
    
        for($i=0;$i<$adResult['count'];$i++){
            $RID = unpack('V',substr($adResult[$i]['objectsid'][0],24));    //Extract the relative SID from the SID
            $RIDs[] = $RID[1];
    
            $resultSet = array();
            foreach($attributes as $attribute){
                $resultSet[$attribute] = $adResult[$i][$attribute][0];
            }
            $result[$RID[1]] = $resultSet;
        }
    
        $nextStepGroupRID = $nextStepGroupRID+$searchStep;
    
    }while($adResult['count']>1);
    
    var_dump($result);
    

    $ adConn-&gt;搜索方法如下所示:

    function search($filter, $attributes = false, $base_dn = null) {
            if(!isset($base_dn)){
                $base_dn = $this->baseDN;
            }
    
            $entries = false;
            if (is_string($filter) && $this->bind) {
                    if (is_array($attributes)) {
                            $search  = ldap_search($this->resource, $base_dn, $filter, $attributes);
                    } else {
                            $search  = ldap_search($this->resource, $base_dn, $filter);
                    }
                    if ($search !== false) {
                            $entries = ldap_get_entries($this->resource, $search);
                    }
            }
            return $entries;
    }
    

答案 2 :(得分:1)

当最近的SID之间的距离999更多时,可能发生先前的脚本错误。 示例:

S-1-5-21-3188256696-111411151-3922474875- 1158

S-1-5-21-3188256696-111411151-3922474875- 3359

3359-1158&gt; 999

为了避免这种情况,你需要使用刚性结构

示例:

$tt = '1';
do {
    ...
    $nextStepGroupRID = $nextStepGroupRID - $searchStep;
    $tt++;
} while ($tt < '30');

在这个例子中,我们被迫检查999 * 30 * 2 = 59940值。

答案 3 :(得分:1)

我能够使用ldap_control_paged_result

来克服大小限制

ldap_control_paged_result 用于通过发送分页控件来启用LDAP分页。以下功能在我的情况下完美运行。这适用于(PHP 5&gt; = 5.4.0,PHP 7)

function retrieves_users($conn)
    {
        $dn        = 'ou=,dc=,dc=';
        $filter    = "(&(objectClass=user)(objectCategory=person)(sn=*))";
        $justthese = array();

        // enable pagination with a page size of 100.
        $pageSize = 100;

        $cookie = '';

        do {
            ldap_control_paged_result($conn, $pageSize, true, $cookie);

            $result  = ldap_search($conn, $dn, $filter, $justthese);
            $entries = ldap_get_entries($conn, $result);

            if(!empty($entries)){
                for ($i = 0; $i < $entries["count"]; $i++) {
                    $data['usersLdap'][] = array(
                            'name' => $entries[$i]["cn"][0],
                            'username' => $entries[$i]["userprincipalname"][0]
                    );
                }
            }
            ldap_control_paged_result_response($conn, $result, $cookie);

        } while($cookie !== null && $cookie != '');

        return $data;
    }

如果您现在已成功更新服务器,则上述功能可以获取所有条目。我正在使用此功能来获取AD中的所有用户。