Tôi đã bị tấn công bởi cùng một vấn đề trong khi phát triển Zend_Ldap cho khung công tác Zend. Tôi sẽ cố gắng giải thích vấn đề thực sự là gì, nhưng để ngắn gọn: cho đến khi PHP 5.4, không thể sử dụng kết quả được phân trang từ Active Directory với phiên bản PHP chưa được vá (ext/ldap
) do giới hạn chính xác tiện ích mở rộng.
Hãy cố gắng làm sáng tỏ toàn bộ điều ... Microsoft Active Directory sử dụng một điều khiển máy chủ được gọi là để thực hiện phân trang kết quả phía máy chủ. Điều khiển này được mô tả trong RFC 2696 "LDAP Control Extension for Simple Paged Results Manipulation".
ext/php
cung cấp quyền truy cập vào tiện ích mở rộng kiểm soát LDAP qua số ldap_set_option()
và tùy chọn LDAP_OPT_SERVER_CONTROLS
và LDAP_OPT_CLIENT_CONTROLS
tương ứng. Để đặt điều khiển phân trang, bạn cần control-oid, là 1.2.840.113556.1.4.319
và chúng tôi cần biết cách mã hóa giá trị điều khiển (điều này được mô tả trong số RFC).Giá trị là một chuỗi octet gói phiên bản BER-mã hóa của trình tự sau đây (sao chép từ RFC):
realSearchControlValue ::= SEQUENCE {
size INTEGER (0..maxInt),
-- requested page size from client
-- result set size estimate from server
cookie OCTET STRING
}
Vì vậy, chúng ta có thể thiết lập sự kiểm soát máy chủ thích hợp trước khi thực hiện truy vấn LDAP:
$pageSize = 100;
$pageControl = array(
'oid' => '1.2.840.113556.1.4.319', // the control-oid
'iscritical' => true, // the operation should fail if the server is not able to support this control
'value' => sprintf ("%c%c%c%c%c%c%c", 48, 5, 2, 1, $pageSize, 4, 0) // the required BER-encoded control-value
);
Điều này cho phép chúng tôi gửi truy vấn được phân trang đến máy chủ LDAP/AD. Nhưng làm thế nào để chúng tôi biết nếu có nhiều trang để làm theo và làm thế nào để chúng tôi xác định với giá trị kiểm soát chúng tôi phải gửi truy vấn tiếp theo của chúng tôi?
Đây là nơi chúng tôi gặp khó khăn ... Máy chủ phản hồi với tập kết quả bao gồm thông tin phân trang yêu cầu nhưng PHP thiếu phương thức để lấy chính xác thông tin này từ tập kết quả. PHP cung cấp một trình bao bọc cho hàm LDAP API ldap_parse_result()
nhưng tham số cuối cùng được yêu cầu serverctrlsp
không được tiếp xúc với hàm PHP, do đó không có cách nào để truy xuất thông tin được yêu cầu. Một bug report đã được nộp cho vấn đề này nhưng vẫn chưa có phản ứng từ năm 2005. Nếu ldap_parse_result()
chức năng cung cấp các thông số cần thiết, sử dụng kết quả paged sẽ làm việc như
$l = ldap_connect('somehost.mydomain.com');
$pageSize = 100;
$pageControl = array(
'oid' => '1.2.840.113556.1.4.319',
'iscritical' => true,
'value' => sprintf ("%c%c%c%c%c%c%c", 48, 5, 2, 1, $pageSize, 4, 0)
);
$controls = array($pageControl);
ldap_set_option($l, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_bind($l, 'CN=bind-user,OU=my-users,DC=mydomain,DC=com', 'bind-user-password');
$continue = true;
while ($continue) {
ldap_set_option($l, LDAP_OPT_SERVER_CONTROLS, $controls);
$sr = ldap_search($l, 'OU=some-ou,DC=mydomain,DC=com', 'cn=*', array('sAMAccountName'), null, null, null, null);
ldap_parse_result ($l, $sr, $errcode, $matcheddn, $errmsg, $referrals, $serverctrls); // (*)
if (isset($serverctrls)) {
foreach ($serverctrls as $i) {
if ($i["oid"] == '1.2.840.113556.1.4.319') {
$i["value"]{8} = chr($pageSize);
$i["iscritical"] = true;
$controls = array($i);
break;
}
}
}
$info = ldap_get_entries($l, $sr);
if ($info["count"] < $pageSize) {
$continue = false;
}
for ($entry = ldap_first_entry($l, $sr); $entry != false; $entry = ldap_next_entry($l, $entry)) {
$dn = ldap_get_dn($l, $entry);
}
}
Như bạn thấy có một dòng mã (*)
làm cho toàn bộ điều vô dụng. Trên con đường của tôi mặc dù thông tin thưa thớt về chủ đề này, tôi tìm thấy một bản vá chống lại PHP 4.3.10 ext/ldap
bởi Iñaki Arenaza nhưng tôi cũng không thử nó cũng không biết liệu bản vá có thể được áp dụng trên PHP5 ext/ldap
hay không. Các miếng vá kéo dài ldap_parse_result()
để lộ các thông số 7 đến PHP:
--- ldap.c 2004-06-01 23:05:33.000000000 +0200
+++ /usr/src/php4/php4-4.3.10/ext/ldap/ldap.c 2005-09-03 17:02:03.000000000 +0200
@@ -74,7 +74,7 @@
ZEND_DECLARE_MODULE_GLOBALS(ldap)
static unsigned char third_argument_force_ref[] = { 3, BYREF_NONE, BYREF_NONE, BYREF_FORCE };
-static unsigned char arg3to6of6_force_ref[] = { 6, BYREF_NONE, BYREF_NONE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE };
+static unsigned char arg3to7of7_force_ref[] = { 7, BYREF_NONE, BYREF_NONE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE };
static int le_link, le_result, le_result_entry, le_ber_entry;
@@ -124,7 +124,7 @@
#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP
PHP_FE(ldap_get_option, third_argument_force_ref)
PHP_FE(ldap_set_option, NULL)
- PHP_FE(ldap_parse_result, arg3to6of6_force_ref)
+ PHP_FE(ldap_parse_result, arg3to7of7_force_ref)
PHP_FE(ldap_first_reference, NULL)
PHP_FE(ldap_next_reference, NULL)
#ifdef HAVE_LDAP_PARSE_REFERENCE
@@ -1775,14 +1775,15 @@
Extract information from result */
PHP_FUNCTION(ldap_parse_result)
{
- pval **link, **result, **errcode, **matcheddn, **errmsg, **referrals;
+ pval **link, **result, **errcode, **matcheddn, **errmsg, **referrals, **serverctrls;
ldap_linkdata *ld;
LDAPMessage *ldap_result;
+ LDAPControl **lserverctrls, **ctrlp, *ctrl;
char **lreferrals, **refp;
char *lmatcheddn, *lerrmsg;
int rc, lerrcode, myargcount = ZEND_NUM_ARGS();
- if (myargcount 6 || zend_get_parameters_ex(myargcount, &link, &result, &errcode, &matcheddn, &errmsg, &referrals) == FAILURE) {
+ if (myargcount 7 || zend_get_parameters_ex(myargcount, &link, &result, &errcode, &matcheddn, &errmsg, &referrals, &serverctrls) == FAILURE) {
WRONG_PARAM_COUNT;
}
@@ -1793,7 +1794,7 @@
myargcount > 3 ? &lmatcheddn : NULL,
myargcount > 4 ? &lerrmsg : NULL,
myargcount > 5 ? &lreferrals : NULL,
- NULL /* &serverctrls */,
+ myargcount > 6 ? &lserverctrls : NULL,
0);
if (rc != LDAP_SUCCESS) {
php_error(E_WARNING, "%s(): Unable to parse result: %s", get_active_function_name(TSRMLS_C), ldap_err2string(rc));
@@ -1805,6 +1806,29 @@
/* Reverse -> fall through */
switch(myargcount) {
+ case 7 :
+ zval_dtor(*serverctrls);
+
+ if (lserverctrls != NULL) {
+ array_init(*serverctrls);
+ ctrlp = lserverctrls;
+
+ while (*ctrlp != NULL) {
+ zval *ctrl_array;
+
+ ctrl = *ctrlp;
+ MAKE_STD_ZVAL(ctrl_array);
+ array_init(ctrl_array);
+
+ add_assoc_string(ctrl_array, "oid", ctrl->ldctl_oid,1);
+ add_assoc_bool(ctrl_array, "iscritical", ctrl->ldctl_iscritical);
+ add_assoc_stringl(ctrl_array, "value", ctrl->ldctl_value.bv_val,
+ ctrl->ldctl_value.bv_len,1);
+ add_next_index_zval (*serverctrls, ctrl_array);
+ ctrlp++;
+ }
+ ldap_controls_free (lserverctrls);
+ }
case 6 :
zval_dtor(*referrals);
if (array_init(*referrals) == FAILURE) {
Trên thực tế lựa chọn duy nhất còn lại sẽ được thay đổi cấu hình Active Directory và nâng cao giới hạn kết quả tối đa. Tùy chọn có liên quan được gọi là MaxPageSize
và có thể được thay đổi bằng cách sử dụng ntdsutil.exe
- vui lòng xem "How to view and set LDAP policy in Active Directory by using Ntdsutil.exe".
EDIT (tham chiếu đến COM):
Hoặc bạn có thể đi theo chiều ngược lại và sử dụng COM-cách tiếp cận qua ADODB như đề xuất trong link cung cấp bởi eykanal.
Câu trả lời tuyệt vời! Cảm ơn rất nhiều, điều này đã giúp tôi rất nhiều! – Christian
Trạng thái của việc này là gì khi PHP 5.4 hỗ trợ các kết quả LDAP được phân trang? – Squazic
@Squazic: Xem câu trả lời http://stackoverflow.com/a/10140166/11354 xuống dưới đây. Dường như có thể làm được bắt đầu với PHP 5.4. –