sử dụng hàm này, đầu tiên phải copy đối tượng cần chỉnh sửa, sau đĩ chỉnh sửa và gởi cho server. Hàm modify( ) cho phép ta xác định một danh sách các thay đổi, và thực hiện thay đổi trực tiếp trên server.
4.3.1 Net::LDAP::Entry
Chúng ta bắt đầu bài tập mới bằng việc viết một hàm tùy chọn in. Tên nĩ là DumpEntry( ). Nĩ sẽ in các nội dung DN theo sau bởi mỗi giá trị của mỗi thuộc tính mà nĩ chứa. ví dụ:
sub DumpEntry { my ( $entry ) = @_; my ( $attrib, $val ); print $entry->dn( ), "\n";
foreach $attrib in ( $entry->attributes( ) ) { foreach $val in ( $entry->get_value( $attrib ) ) { print $attrib, ": ", $val, "\n";
} } } }
Đoạn code này cĩ 3 hàm dn( )
Nếu khơng cĩ tham số, dn ( ) sẽ trả về tên của thư mục là 1 chuỗi ký tự. Nếu cĩ đối số, thì đối số đĩ sẽ được dùng để thiết lập nội dung của DN.
attributes( )
Hàm này trả về một mảng chứa các thuộc tính của nội dung. get_value( )
Chấp nhận một tên thuộc tính và trả một mảng của giá trị của thuộc tính đĩ.
DumpEntry( ) tương tự như dump( ), nĩ chỉ in các thuộc tính và các giá trị được lưu trong bản copy
cục bộ của đối tượng Net::LDAP::Entry. Các thuộc tính thêm vào cĩ thể được lưu trữ trong thư mục.
Ba cách để thao tác với các thuộc tính và giá trị của mục: add( ), delete( ), and replace( ). add( ) chèn một thuộc tính hoặc một giá trị mới vào đối tượng. Đoạn code sau thêm một địa chỉ email của một mục được đại diện bởi hằng số $e. Nếu thuộc tính khơng cĩ sẵn trong nội dung, thì nĩ được thêm vào. Cịn nếu thuộc tính đã cĩ sẵn, thì giá trị mới sẽ được thêm vào thay cho giá trị cũ.
$e->add ( "mail" => "jerry@plainjoe.org" );
Hàm add( )chỉ làm việc trên bản copy cục bộ của nội dung. Nếu thuộc tính mail khơng được hỗ
trợ bởi các lớp đối tượng được đăng ký trong nội dung, bạn khơng thể tìm thấy chúng cho tới khi bạn trả các nội dung về server thư mục. Tương tự, add( ) cho phép bạn gán nhiều giá trị cho một thuộc tính chỉ cho phép cĩ một giá trị duy nhất (ví dụ, thuộc tính uidNumber trong
posixAccount).
Nhiều giá trị cĩ thể được gán cho một thuộc tính bằng cách dùng:
$e->add( "mail" => [ "jerry@plainjoe.org", "jerry@samba.org"] );
"cn" => "Gerald Carter" );
Để xĩa một thuộc tính trong một mục địa phương, chỉ cần gọi hàm delete ( ). Hàm này chấp
nhận các tên thuộc tính cần xĩa, hoặc như là một giá trị vơ hướng, hoặc như là một mảng.
$e->delete ( [ "mail", "cn" ] );
Nĩ cĩ thể xĩa một giá trị cá nhân từ một thuộc tính cĩ nhiều giá trị bằng cách đưa ra một mảng các mục cần xĩa. Ví dụ dưới đây xĩa email jerry@samba.org từ địa chỉ email của mục:
$e->delete( mail => [ "jerry@samba.org" ] );
Cuối cùng, bạn cĩ thể xĩa một thuộc tính và các giá trị của chúng và thay thế nĩ bằng cách gọi
replace( ). Cách này chấp nhận 1 cặp thuộc tính/giá trị giống như là hàm add ( ). Đoạn code dưới
đây thay thể tất cả các giá trị gán cho thuộc tính mail bằng một địa chỉ email mới là
jerry@plainjoe.org.
$e->replace( "mail" => "jerry@plainjoe.org" );
Khi bạn làm việc với các đối tượng Net::LDAP::Entry, nhớ rằng các ví dụ khách hàng chỉ là bản sao, các thay đổi bạn làm chỉ ảnh hưởng đến bản copy cục bộ của nội dung. Phần tiếp theo chỉ dẫn cách để dẫn các thay đổi đĩ vào trong thư mục.
4.3.2 Đẩy bản cập nhật nội dung về server
Khơng cĩ một thay đổi nào ở bản copy cục bộ của đối tượng Net::LDAP::Entry được phản ánh trong thư mục cho tới khi gọi hàm update ( ). Để chỉ cách làm thế nào để cập nhật thư mục, ta sẽ dùng một đoạn mã đơn giản khi một user muốn thay đổi mật khẩu. Đoạn mã này cĩ hai giả định:
• Tất cả các user đều cĩ mục trong thư mục, một tên đăng nhập sẽ khớp với thuộc tính uid
• Tất cả các user đều cĩ thể cập nhật giá trị thuộc tính userPassword
Bạn cần thêm 2 modun cho chương trình này. Term::Readkey giúp bạn đọc loại user mà khơng cần hiển thị trên màn hình. Digest::MD5cho phép tạo ra một đoạn mã hĩa Base64. Đoạn mã bắt đầu như sau:
#!/usr/bin/perl
use Net::LDAP; use Term::ReadKey;
use Digest::MD5 qw(md5_base64);
Bạn cĩ được tên đăng nhập bằng cách tìm trong UID của các chương trình đang chạy:
$username = getpwuid($>);
print "Changing password for user ", $username, "\n";
Sau đĩ đoạn mã sẽ thực thi các thiết lập kết nối LDAP quen thuộc:
ldap = Net::LDAP->new( "ldap.plainjoe.org", version => 3)
or die $!;
$result = $ldap->start_tls( );
die $result->error( ) if $result->code( );
Tiếp theo, chương trình ngầm liên kết với các thư mục ẩn danh và cố gắng xác định vị trí các mục nhập cho người sử dụng. Câu truy vấn là một tìm kiếm cây con bằng cách sử dụng các bộ lọc (uid = $ username). Nếu tìm thấy nhiều kết quả phù hợp, nĩ sẽ trả về mục đầu tiên. Nếu khơng tìm thấy, đoạn mã sẽ báo và thốt.
$msg = $ldap->search(
filter => "(uid=$username)" ); die $msg->error( ) if $msg->code( );
die "No such user in directory [$username]!\n" if !$msg->count;
Nếu bạn biết user cĩ trong thư mục LDAP, nhắc nhở user gõ mật khẩu cũ và mật khẩu mới. Yêu cầu gõ mật khẩu mới 2 lần, và kiểm tra 2 lần gõ như nhau:
## Read old and new password strings. Use ReadMode to prevent the passwords from ## being echoed to the screen.
ReadMode( 'noecho' ); print "Enter Old Password: ";
$old_passwd = chomp( ReadLine(0) ); print "\nEnter New Password: "; $new_passwd = chomp( ReadLine(0) ); print "\nEnter New Password again: "; $new_passwd2 = chomp( ReadLine(0) ); print "\n";
ReadMode( 'restore' );
## Check that new password was typed correctly. if ( "$new_passwd" ne "$new_passwd2" ) { print "New passwords do not match!\n"; exit (1);
}
Để chuyển kết quả tìm kiếm thành một đối tượng, đoạn mã gọi entry ( ). Chương trình con này nhận một chỉ số nguyên vào mảng các mục tạo ra bởi tìm kiếm trước đĩ. Trong trường hợp này, chúng ta chỉ làm việc với mục đầu tiên, chúng ta đang giả sử rằng việc tìm kiếm sẽ chỉ trả về một mục:
$entry = $msg->entry(0);
Bây giờ bạn đã cĩ cả DN của mục của user và cả giá trị của mật khẩu cũ. Bây giờ bạn cĩ thể xác thực người dùng bằng cách liên hệ với server thư mục. Nếu liên kết bị fail, đoạn mã sẽ thơng báo rằng mật khẩu cũ bị sai:
$result = $ldap->bind( $entry->dn( ), password => $old_passwd );
die "Old Password is invalid!\n" if $result->code( );
Cơng việc cịn lại là update mật khẩu của user vào thư mục.
## Generate Base64 md5 hash of the new passwd.
$md5_pw = "{MD5}" . md5_base64($new_passwd) . "= =";
“= =” là để đảm bảo chuỗi ký tự là một bội số của 4 bytes. Tiếp theo, viết đè lên mật khẩu cũ bằng cách gọi hàm replace( ):
$entry->replace( userPassword => $md5_pw );
Để chuyển các thay đổi này vào thư mục, gọi update( ).
$result = $entry->update( $ldap ); die $result->error( ) if $result->code( );
Bây giờ thơng báo cho user là mật khẩu của user đã được update và thốt:
print "Password updated successfully\n"; exit (0);
Changing password for user jerry Enter Old Password: secret Enter New Password: new-secret Enter New Password again: new-secret Password updated successfully
4.3.3 Thay đổi nội dung thư mục:
Mặc dù LDAPv3 khơng đặc biệt hỗ trợ cho việc thực hiện trên nhiều mục 1 lúc, nhưng RFCs xác định rằng các thay đổi trên một mục riêng biệt nào đĩ phải được làm một cách tự động. Khi nào chúng ta quan tâm tới việc update tự động? Giả sử rằng, trong mạng của bạn, tất cả các tài khoản user được tạo trong thư mục LDAP trung tâm, bằng cách sử dụng lớp đối tượng posixAccount.
Nhưng ở trong một mạng lớn, bạn cĩ nhiều admin, mỗi admin lại cĩ thể quản trị trong 1 khoảng thời gian bất kỳ. Bạn cần đảm bảo rằng các cơng cụ quản lý user luơn luơn cĩ được số UID và GID khả dụng tiếp theo mà khơng cĩ sự trùng lặp số ID khi 2 đoạn mã cùng chạy 1 lúc.
Ở đây, việc dùng thư mục để lưu trữ các giá trị UID và GID khả dụng được xem như là việc khờ khạo. Tất cả những gì bạn cần là chương trình con lấy số ID tiếp theo và lưu trữ giá trị tăng dần. Các hoạt động này phải là tự động. Để làm được điều này, bạn phải khai báo 2 lớp đối tượng mới, một cho uidPool và một cho gidPool. Giản đồ cho 2 đối tượng này được minh họa trong hình 10-1 sau:
Đây là cách thực thi của hàm get_next_uid( ). Nĩ yêu cầu đối tượng Net::LDAP xem nĩ như là tham số duy nhất. get_next_gid( ) hầu như giống nhau;
########################################################### Get the next available UID from the idPool. Spin until you get one. ## Get the next available UID from the idPool. Spin until you get one. ##
sub get_next_uid { my ( $ldap ) = @_; my ( $uid, $msg, $entry );