2011-12-26 185 views
3

我对查询Microsoft的Active Directory并遇到一些困难颇为陌生:PHP超过ldap_search大小限制

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

我目前遇到了两个可行的解决方案:通过的objectSID

  1. 排序项并使用过滤器来获取所有对象。 Sample Code
    我不喜欢有以下几个原因:
    • 似乎不可预知与混乱的objectSID,因为你必须把它拆开,将其转换为十进制,将其转换回...
    • 我没有看到你如何比较这些ID。
      (我试图: '&((objectClass的=用户)(的objectSID> = 0))')的对象的名称(如建议here)的第一个字母之后

  2. 过滤:
    这不是一个最佳的解决方案,因为我们系统中的许多用户/组都以前缀相同的几个字母为前缀。

所以我的问题:

什么方法是最好这里使用?
如果是第一个,我该如何确保正确处理objectSid?

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

更新:
- This相关的问题提供了有关为什么简单分页结果的扩展不起作用信息。
- Web服务器正在Linux服务器上运行,因此COM对象/ adoDB不是一个选项。

回答

1

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

此变通方法有它的局限性:

  1. 它仅适用于物体与的objectSID,即用户和组。
  2. 它假设所有用户/组都由相同的权限创建。
  3. 它假定不存在比大小限制更多的丢失相对SID。

这个想法是先读取所有可能的对象,然后选出具有最低相对SID的对象。相对SID是在SID的最后一个块:

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

让我们假设这是一个搜索最低相对SID,只有返回'部分搜索结果'。 让我们进一步假设的大小限制为1000

程序然后执行以下操作: 它搜索

S-1-5-21-3188256696-111411151-3922474875- 1158之间与SID的所有对象

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

然后所有之间

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

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

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

这种方法有几个问题,但对我的目的来说就足够了。
守则:

$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->的搜索方法是这样的:可能出现

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; 
} 
1

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

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

+1

感谢关键字'简单的分页结果'导致[此问题](http://stackoverflow.com/questions/1473075/enumerate-all-users-in-ldap-with-php),它有一个伟大的回答。似乎做到这一点的唯一方法是adoDB,好奇这是如何解决的。 – Envyrus 2011-12-27 07:23:13

+1

你的链接不见了。 – MrUpsidown 2014-09-30 07:12:46

1

前面的脚本错误时,最近的SID 999多之间的距离。 实施例:

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

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

3359 -1158> 999

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

实施例:

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

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

0

我能得到周围使用ldap_control_paged_result

ldap_control_paged_result用于发送分页控制启用LDAP分页大小限制。以下功能在我的情况下完美工作。这将工作(PHP 5> = 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; 
    } 

如果您已成功现在更新您的服务器,然后上面的功能可以得到所有的条目。我正在使用此功能来获取我们广告中的所有用户。