Monday, September 15, 2014

Migrating WebLogic Embedded LDAP to OpenLDAP

Oracle WebLogic memiliki Embedded LDAP yang berfungsi sebagai penyedia otentikasi, otorisasi, credential mapping, dan role mapping. Ketika sebuah WebLogic Domain dibuat, secara default user dan group administratif seperti user weblogic tersimpan di WebLogic Embedded LDAP server. Secara fungsi, Embedded LDAP sudah menyediakan fungsi yang lengkap untuk WebLogic Domain. Namun pada ada kasus di mana menggunakan Embedded LDAP kurang disarankan, terutama pada deployment yang memiliki user lebih dari 10.000 user. Solusinya adalah dengan memindahkan LDAP service keluar dari WebLogic Domain.

Pada artikel kali ini saya ingin berbagi langkah-langkah untuk memigrasikan user yang tersimpan di WebLogic Embedded LDAP ke OpenLDAP. Pada dasarnya aktivitas migrasinya bisa dilihat pada diagram aktivitas berikut ini:
Activity Diagram for Embedded LDAP to OpenLDAP Migration

Penjelasan detilnya adalah:
  1. Shutdown, Backup, and Startup WebLogic Domain
    Langkah wajib setiap kali kita akan melakukan perubahan yang sifatnya domain-wide. Full backup dapat dilakukan dengan membuat tarball dari direktori di mana WebLogic Domain dan Oracle binary berada, diikuti dengan backup skema database-nya. Jika sudah selesai melakukan backup, jalankan kembali cukup AdminServer saja.
  2. Export myrealm
    Login ke Administration Console, pilih $NAMA_DOMAIN -> Security Realms -> myrealm -> Migration -> Export. Tulis nama direktori tempat hasil export akan disimpan, kemudian tekan tombol Save. Aktivitas ini akan menghasilkan beberapa file, namun yang perlu kita proses adalah file DefaultAuthenticator.dat yang sebenarnya merupakan file dengan format LDIF.
    Export DefaultAuthenticator
  3. Parse DefaultAuthenticator.dat and Generate OpenLDAP LDIF
    Pada tahap ini saya akan menggunakan Perl script sederhana yang memanfaatkan kekuatan regex untuk melakukan parsing file DefaultAuthenticator.dat dan mengubah formatnya menjadi OpenLDAP-friendly. Tidak semua field ter-cover pada script ini, namun cukup untuk kasus yang saya temui. Output dari script in akan menghasilkan beberapa file, namun yang penting adalah file people.ldif yang berisi seluruh user dan groups.ldif yang berisi seluruh group serta mapping-nya terhadap user yang ada.
     #!/usr/bin/perl -w  
       
     # Author : Okky Hendriansyah (okky@nostratech.com)  
     # Date   : 2014/07/14  
     # Desc.  : Oracle WebLogic Embedded LDAP LDIF Converter to OpenLDAP LDIF  
     # Input  : DefaultAuthenticator.dat exported from WebLogic Administration Console  
     # Output : processed/excluded.ldif, processed/groups.ldif, processed/people.ldif, and processed/people_rdn.ldif  
     # Run    : ./weblogic_to_openldap.pl  
       
     # Change this constant to resemble the directory root tree  
     use constant ORGANIZATION => "dc=nostra,dc=tech";  
       
     # Open the exported DefaultAuthenticator LDIF file and slurp the whole contents  
     open FILEHANDLE, '<DefaultAuthenticator.dat' or die $!;  
     my $default_authenticator = do { local $/; <FILEHANDLE>; };  
     close FILEHANDLE;  
       
     # Open output files  
     open EXCLUDED_ENTRIES, '>processed/excluded.ldif' or die $!;  
     open GROUP_ENTRIES, '>processed/groups.ldif' or die $!;  
     open GROUP_RDN_ENTRIES, '>processed/groups_rdn.ldif' or die $!;  
     open PERSON_ENTRIES, '>processed/people.ldif' or die $!;  
     open PERSON_RDN_ENTRIES, '>processed/people_rdn.ldif' or die $!;  
       
     # Split LDAP entries to array elements  
     @ldap_entries = split /\n\n/, $default_authenticator;  
     $total_ldap_entries = scalar @ldap_entries;  
       
     # Variables initialization  
     $total_groups = 0;  
     $total_people = 0;  
     $total_excluded = 0;  
     my %groups;  
       
     # Traverse all the LDAP entries  
     foreach $ldap_entry (@ldap_entries) {  
         # Initial parse group entries  
         if ($ldap_entry =~ m/objectclass: groupOfUniqueNames/i) {  
             # RDN  
             if ($ldap_entry =~ m/dn: cn=([^,]+),ou=groups,ou=\@realm\@,dc=\@domain\@/i) {  
                 # Register the group  
                 $groups{$1} = qw();  
             }  
         }  
         # Parse person entries  
         elsif ($ldap_entry =~ m/objectclass: inetOrgPerson/i) {  
             # Person total  
             $total_people++;  
             # RDN  
             if ($ldap_entry =~ m/dn: uid=([^,]+),ou=people,ou=\@realm\@/) {  
                 $person_uid_value = $1;  
                 $person_rdn = "dn: uid=$person_uid_value,ou=people," . ORGANIZATION . "\n";  
                 $output = $person_rdn;   
                 # objectclass  
                 $person_objectclass = "objectClass: inetOrgPerson\n";  
                 $person_objectclass .= "objectClass: organizationalPerson\n";  
                 $person_objectclass .= "objectClass: person\n";  
                 $person_objectclass .= "objectClass: top\n";  
                 $output .= $person_objectclass;  
                 # Group  
                 while ($ldap_entry =~ m/wlsMemberOf: cn=([^,]+),ou=groups,ou=\@realm\@/gi) {  
                     push @{$groups{$1}}, $person_uid_value;  
                 }  
                 # description  
                 if ($ldap_entry =~ m/description: ([^\n]+)/i) {  
                     $person_description = "description: $1\n";  
                     $output .= $person_description;  
                 }  
                 # cn  
                 if ($ldap_entry =~ m/cn: ([^\n]+)/i) {  
                     $person_cn = "cn: $1\n";  
                     $output .= $person_cn;  
                 }  
                 # sn  
                 if ($ldap_entry =~ m/sn: ([^\n]+)/i) {  
                     $person_sn = "sn: $1\n";  
                     $output .= $person_sn;  
                 }  
                 # uid  
                 if ($ldap_entry =~ m/uid: ([^\n]+)/i) {  
                     $person_uid = "uid: $1\n";  
                     $output .= $person_uid;  
                 }  
                 # userpassword  
                 if ($ldap_entry =~ m/userpassword:: ([^\n]+)/i) {  
                     $person_userpassword = "userPassword:: $1\n";  
                     $output .= $person_userpassword;  
                 }  
                 # mail  
                 if ($ldap_entry =~ m/mail: ([^\n]+)/i) {  
                     $person_mail = "mail: $1\n";  
                     $output .= $person_mail;  
                 }  
                 # displayname  
                 if ($ldap_entry =~ m/displayname: ([^\n]+)/i) {  
                     $person_displayname = "displayName: $1\n";  
                     $output .= $person_displayname;  
                 }  
                 # givenname  
                 if ($ldap_entry =~ m/givenname: ([^\n]+)/i) {  
                     $person_givenname = "givenName: $1\n";  
                     $output .= $person_givenname;  
                 }  
                 # title  
                 if ($ldap_entry =~ m/title: ([^\n]+)/i) {  
                     $person_title = "title: $1\n";  
                     $output .= $person_title;  
                 }  
                 # departmentnumber  
                 if ($ldap_entry =~ m/departmentnumber: ([^\n]+)/i) {  
                     $person_departmentnumber = "departmentNumber: $1\n";  
                     $output .= $person_departmentnumber;  
                 }  
                 # employeenumber  
                 if ($ldap_entry =~ m/employeenumber: ([^\n]+)/i) {  
                     $person_employeenumber = "employeeNumber: $1\n";  
                     $output .= $person_employeenumber;  
                 }  
                 # employeetype  
                 if ($ldap_entry =~ m/employeetype: ([^\n]+)/i) {  
                     $person_employeetype = "employeeType: $1\n";  
                     $output .= $person_employeetype;  
                 }  
                 # o  
                 if ($ldap_entry =~ m/o: ([^\n]+)/i) {  
                     $person_o = "o: $1\n";  
                     $output .= $person_o;  
                 }  
                 # preferredlanguage  
                 if ($ldap_entry =~ m/preferredlanguage: ([^\n]+)/i) {  
                     $person_preferredlanguage = "preferredLanguage: $1\n";  
                     $output .= $person_preferredlanguage;  
                 }  
                 # telephonenumber  
                 if ($ldap_entry =~ m/telephonenumber: ([^\n]+)/i) {  
                     $person_telephonenumber = "telephoneNumber: $1\n";  
                     $output .= $person_telephonenumber;  
                 }  
                 # jpegphoto  
                 if ($ldap_entry =~ m/jpegphoto:: ([^\n]+)/i) {  
                     $person_jpegphoto = "jpegPhoto:: $1\n";  
                     $output .= $person_jpegphoto;  
                 }  
                 # Print to file  
                 print PERSON_ENTRIES "$output\n";  
                 print PERSON_RDN_ENTRIES "uid=$person_uid_value,ou=people," . ORGANIZATION . "\n";  
             }  
         }  
         # No need to parse these entries   
         else {  
             $total_excluded++;  
             print EXCLUDED_ENTRIES "$ldap_entry\n\n";  
         }  
     }  
       
     # Repeat traversing the LDAP entries but only group entries are processed  
     foreach $ldap_entry (@ldap_entries) {  
         # Compose group entries  
         if ($ldap_entry =~ m/objectclass: groupOfUniqueNames/i) {  
             # Group total  
             $total_groups++;  
             # RDN  
             if ($ldap_entry =~ m/dn: cn=([^,]+),ou=groups,ou=\@realm\@,dc=\@domain\@/i) {  
                 $group_cn_value = $1;  
                 $group_rdn = "dn: cn=$group_cn_value,ou=groups," . ORGANIZATION . "\n";  
                 $output = $group_rdn;  
                 # objectclass  
                 $group_objectclass = "objectClass: groupOfUniqueNames\n";  
                 $group_objectclass .= "objectClass: top\n";  
                 $output .= $group_objectclass;  
                 # cn  
                 if ($ldap_entry =~ m/cn: ([^\n]+)/i) {  
                     $group_cn = "cn: $1\n";  
                     $output .= $group_cn;  
                 }  
                 # description  
                 if ($ldap_entry =~ m/description: ([^\n]+)/i) {  
                     $group_description = "description: $1\n";  
                     $output .= $group_description;  
                 }  
                 # uniqueMember  
                 $group_uniquemember = "";  
                 foreach $member_uid (@{$groups{$group_cn_value}}) {  
                     $group_uniquemember .= "uniqueMember: uid=$member_uid,ou=people," . ORGANIZATION . "\n";  
                 }  
                 # handles groups without any member defined  
                 if ($group_uniquemember eq "") {  
                     $group_uniquemember .= "uniqueMember: \n";  
                 }  
                 $output .= $group_uniquemember;  
                 # Print to file  
                 print GROUP_ENTRIES "$output\n";  
                 print GROUP_RDN_ENTRIES "cn=$group_cn_value,ou=groups," . ORGANIZATION . "\n";  
             }  
         }  
         # Ignore if encounter the first entry of a person  
         elsif ($ldap_entry =~ m/objectclass: inetOrgPerson/i) {  
             # Skip all person entries  
             last;  
         }  
     }  
       
     # Close output files  
     close EXCLUDED_ENTRIES;  
     close GROUP_ENTRIES;  
     close GROUP_RDN_ENTRIES;  
     close PERSON_ENTRIES;  
     close PERSON_RDN_ENTRIES;  
       
     # Print summary  
     print "Total Groups\t: $total_groups\n";  
     print "Total People\t: $total_people\n";  
     print "Total Excluded\t: $total_excluded\n";  
     print "Total Entries\t: $total_ldap_entries\n";  
    
  4. Import People and Groups
    Jalankan LDAP browser seperti Apache Directory Studio, koneksikan dengan OpenLDAP server yang sudah running. Import terlebih dahulu file people.ldif dilanjutkan oleh groups.ldif
  5. Recreate Admin Users in OpenLDAP
    Langkah ini opsional, kita juga dapat menggunakan user weblogic yang ter-import melalui langkah sebelumnya.
  6. Reassociate Authentication Provider
    Login kembali ke Administration Console. Pilih $NAMA_DOMAIN -> Security Realms -> myrealm -> Providers. Buat authentication provider baru dengan cara tekan tombol New dan masukkan tipe OpenLDAPAuthenticator. Pilih OpenLDAPAuthenticator tersebut dan ubah Control Flag menjadi SUFFICIENT. Setelah save, pilih tab Provider Specific dan masukkan detil informasi ke OpenLDAP server. Save setelah selesai memasukkan semua informasi tersebut. Kembali ke halaman Providers, masuk ke DefaultAuthenticator dan ganti Control Flag menjadi SUFFICIENT dan ubah urutan provider menjadi OpenLDAPAuthenticator, DefaultAuthenticator, dan DefaultIdentityAsserter.
  7. Restart WebLogic Domain
    Activate changes dan restart WebLogic Domain agar perubahan tersebut dapat diaplikasikan.
  8. Remap Global Roles, especially Admin
    Langkah ini tidak diperlukan jika kita tidak melakukan langkah ke-5.
  9. Enable Identity Virtualization
    Login ke WebLogic Enterprise Manager Fusion Middleware Control. Pilih $NAMA_DOMAIN -> WebLogic Domain -> Security -> Security Provider Configuration. Buka bagian Identity Store Provider kemudian pilih Configure. Tambahkan custom property virtualize dengan nilai true kemudian Save.
    Enable Identity Virtualization
  10. Restart WebLogic Domain
    Activate changes dan restart WebLogic Domain agar perubahan tersebut dapat diaplikasikan.
  11. Reassociate Application-specific Roles/Group
    Remap role dan grup pada masing-masing aplikasi, agar tidak lagi menggunakan grup yang ada di Embedded LDAP.
  12. Test and Validate
    Ganti password user yang tersimpan pada OpenLDAP dan lakukan aktivitas login dan logout baik ke Administration Console maupun ke aplikasi yang menggunakan WebLogic Authentication Provider. Seharusnya sekarang sudah bisa login menggunakan user dan password yang sesuai dengan OpenLDAP.
Selamat mencoba! :D


No comments:

Post a Comment