2011-12-26 101 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; 
    } 

如果您已成功現在更新您的服務器,然後上面的功能可以得到所有的條目。我正在使用此功能來獲取我們廣告中的所有用戶。

相關問題