11 Replies Latest reply: Mar 13, 2009 7:14 AM by 800479 RSS

    JNDI, Active Directory and objectGUID's

    800477
      For some reason my original post on this topic was dropped from the forum.

      Active Directory and for that matter Active Directory Application Mode (ADAM) allow most objects to be renamed or moved. There are internal mechanisms to ensure the integrity of the directory which make objects rename safe.

      For example assume there is a user object cn=Jenny Smith ,ou=Finance,dc=antipodes,dc=com and she is a member of the group cn=Tennis Players,ou=Social Club,dc=antipodes,dc=com

      If Jenny gets married, and changes jobs, her distinguished name becomes cn=Jenny Smith-Jones,ou=Sales,dc=antipodes,dc=com.

      Because AD is rename safe, her group memberships are maintained.

      However how does an application keep track of directory objects as they are renamed or moved? For every directory object there is one attribute called objectGUID which is unique and invariant. objectGUID's are created by the system and are guaranteed to be globally unique.

      The following sample code demonstrates retrieving the objectGUID attribute and displaying it in both the binary and string form.
      /**
       * searchforguid.java
       * 5 July 2001
       * Sample JNDI application to perform a search against the Active Directory
       * and also return the objectGUID in both binary and string formats
       * 
       */
      
      import java.util.Hashtable;
      import javax.naming.ldap.*;
      import javax.naming.directory.*;
      import javax.naming.*;
      
      
      public class searchforguid     {
           public static void main (String[] args)     {
           
                Hashtable env = new Hashtable();
                //Can use either DN, NTLM or UPN style credentials
                String adminName = "CN=Administrator,CN=Users,DC=ANTIPODES,DC=COM";
                //String adminName = "Administrator@ANTIPODES.COM";
                //String adminName = "ANTIPODES\\Administrator";
                String adminPassword = "XXXXXX";
                String ldapURL = "ldap://myddc.antipodes.com:389";
                
                env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
      
                //set security credentials, note using simple cleartext authentication
                env.put(Context.SECURITY_AUTHENTICATION,"simple");
                env.put(Context.SECURITY_PRINCIPAL,adminName);
                env.put(Context.SECURITY_CREDENTIALS,adminPassword);
      
                //specify attributes to be returned in binary format
                env.put("java.naming.ldap.attributes.binary","objectGUID");
      
                //connect to my domain controller
                env.put(Context.PROVIDER_URL,ldapURL);
                try {
      
                     //Create the initial directory context
                     LdapContext ctx = new InitialLdapContext(env,null);
      
                     //Create the search controls           
                     SearchControls searchCtls = new SearchControls();
                
                     //Specify the attributes to return
                     String returnedAtts[]={"sn","givenName","mail","objectGUID"};
                     searchCtls.setReturningAttributes(returnedAtts);
                
                     //Specify the search scope
                     searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
      
                     //specify the LDAP search filter
                     String searchFilter = "(&(objectClass=user)(cn=Albert Einstein))";
      
                     //Specify the Base for the search
                     String searchBase = "DC=Antipodes,DC=Com"; 
      
                     //initialize counter to total the results
                     int totalResults = 0;
      
      
                     //Search for objects using the filter
                     NamingEnumeration answer = ctx.search(searchBase, searchFilter, searchCtls);
      
                     //Loop through the search results
                     while (answer.hasMoreElements()) {
                          SearchResult sr = (SearchResult)answer.next();
      
                          totalResults++;
      
                          System.out.println(">>>" + sr.getName());
      
                          // Print out some of the attributes, catch the exception if the attributes have no values
                          Attributes attrs = sr.getAttributes();
                          if (attrs != null) {
                               try {
                                    System.out.println("   name: " + attrs.get("givenName").get() + " " + attrs.get("sn").get());
                                    System.out.println("   mail: " + attrs.get("mail").get());
                                    byte[] GUID = (byte[])attrs.get("objectGUID").get();
                                    String strGUID = "";
                                    String byteGUID = "";
                                    //Convert the GUID into string using the byte format
                                    for (int c=0;c<GUID.length;c++) {
                                         byteGUID = byteGUID + "\\" + AddLeadingZero((int)GUID[c] & 0xFF);
                                    }
                                    //convert the GUID into string format
                                    strGUID = "{";
                                    strGUID = strGUID + AddLeadingZero((int)GUID[3] & 0xFF);
                                    strGUID = strGUID + AddLeadingZero((int)GUID[2] & 0xFF);
                                    strGUID = strGUID + AddLeadingZero((int)GUID[1] & 0xFF); 
                                    strGUID = strGUID + AddLeadingZero((int)GUID[0] & 0xFF);
                                    strGUID = strGUID + "-";
                                    strGUID = strGUID + AddLeadingZero((int)GUID[5] & 0xFF);
                                    strGUID = strGUID + AddLeadingZero((int)GUID[4] & 0xFF);
                                    strGUID = strGUID + "-";
                                    strGUID = strGUID + AddLeadingZero((int)GUID[7] & 0xFF);
                                    strGUID = strGUID + AddLeadingZero((int)GUID[6] & 0xFF);
                                    strGUID = strGUID + "-";
                                    strGUID = strGUID + AddLeadingZero((int)GUID[8] & 0xFF);
                                    strGUID = strGUID + AddLeadingZero((int)GUID[9] & 0xFF);
                                    strGUID = strGUID + "-";
                                    strGUID = strGUID + AddLeadingZero((int)GUID[10] & 0xFF);
                                    strGUID = strGUID + AddLeadingZero((int)GUID[11] & 0xFF);
                                    strGUID = strGUID + AddLeadingZero((int)GUID[12] & 0xFF);
                                    strGUID = strGUID + AddLeadingZero((int)GUID[13] & 0xFF);
                                    strGUID = strGUID + AddLeadingZero((int)GUID[14] & 0xFF);
                                    strGUID = strGUID + AddLeadingZero((int)GUID[15] & 0xFF);
                                    strGUID = strGUID + "}";
                                    System.out.println("GUID (String format): " + strGUID);
                                    System.out.println("GUID (Byte format): " + byteGUID);
      
                               }
                               catch (NullPointerException e)     {
                                    System.err.println("Problem listing attributes: " + e);
                               }
                          
                          }
      
                     }
      
                      System.out.println("Total results: " + totalResults);
                     ctx.close();
      
                } 
                catch (NamingException e) {
                     System.err.println("Problem searching directory: " + e);
                }
           }
      
           static String AddLeadingZero(int k) {
                return (k<0xF)?"0" + Integer.toHexString(k):Integer.toHexString(k);
           }
      
      }
      Now there are two ways to retrieve an object using its objectGUID.

      The first is to simply perform a search using the objectGUID in it's binary format. (Remember that the backslash character is an LDAP special character and must be escaped).

      In this case just modify your application to use a search filter that includes the binary form of the objectGUID
      //specify the LDAP search filter
      //This is the binary format of the objectGUID
      //Note that I've escaped the '\' character
      String searchFilter = "(objectGUID=\\67\\8a\\44\\7c\\3b\\92\\ee\\48\\b2\\1a\\34\\51\\f2\\f7\\58\\ca)";
      To use the string form of the search filter, in older versions of JNDI before they screwed up the LDAP name parsing :-(
      all you needed to do was issue a base level search using the objectGUID as the searchbase, and objectClass=* as the search filter.
      //Specify the search scope
      //Note for GUID=xxxx must use base level search
      searchCtls.setSearchScope(SearchControls.OBJECT_SCOPE);
      
      //specify the LDAP search filter
      //Note for GUID=xxxx must use objectClass filter
      String searchFilter = "(objectClass=*)";
      
      //Specify the Base for the search
      //This is the string form of the GUID without the braces
      String searchBase = "<GUID=7c448a67-923b-48ee-b21a-3451f2f758ca>";
      Unfortunately in current Java releases, this results in a InvalidNamingException because it appears that the NamingEnumeration cannot parse a dn that contains LDAP special characters, in this case the <> characters that enclose the GUID.
      Fortunately you can just simply retrieve the object by binding directly to it, as this sample illustrates.
      /**
       * retrievebyguid.java
       * 5 July 2001
       * Sample JNDI application to retrieve an Active Directory 
       * object using the string form of its objectGUID
       */
      
      import java.util.Hashtable;
      import javax.naming.ldap.*;
      import javax.naming.directory.*;
      import javax.naming.*;
      
      
      public class retrievebyguid     {
           public static void main (String[] args)     {
           
                Hashtable env = new Hashtable();
                String adminName = "CN=Administrator,CN=Users,DC=ANTIPODES,DC=COM";
                String adminPassword = "XXXXXX";
                String ldapURL = "ldap://myddc.antipodes.com:389";
                
                env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
      
                //set security credentials, note using simple cleartext authentication
                env.put(Context.SECURITY_AUTHENTICATION,"simple");
                env.put(Context.SECURITY_PRINCIPAL,adminName);
                env.put(Context.SECURITY_CREDENTIALS,adminPassword);
                          
                //connect to my domain controller
                env.put(Context.PROVIDER_URL,ldapURL);
                try {
      
                     //Create the initial directory context
                     LdapContext ctx = new InitialLdapContext(env,null);
                
                     //Bind directly using the string form of the GUID
                     String strGUID = "<GUID=7c448a67-923b-48ee-b21a-3451f2f758ca>";
      
                     //Specify the attributes to return
                     String returnedAtts[]={"distinguishedName","sn","givenName","mail"};
      
                     Attributes attr = ctx.getAttributes(strGUID,returnedAtts);
      
                     //print out the retrieved attributes
                     System.out.println("DN: " + attr.get("distinguishedName").get());
                     System.out.println("Name: " + attr.get("givenName").get() + " " + attr.get("sn").get());
                     System.out.println("Mail: " + attr.get("mail").get());
                     
                     ctx.close();
      
                }
                catch (NamingException e) {
                     System.err.println("Problem searching directory: " + e);
                }
           }
      }
        • 1. Re: JNDI, Active Directory and objectGUID's
          843793
          Hi Steven,
          From the above code, I manage to get the objectGUID and gets it string form and save it to the database. I tried various ways to query it using the examples you had provided to get back to the object
          It always gave me an exception.

          I had my objectGUID to be returned in the binary form even while comparing once I had that stuff in the String form.

          It did not work. Now this string in maintained in the database, is there any way to sort of reverse engineer it and obtain the same binary string as it orginally was, then I could use a searchfilter and obtain that object.

          One more query in your AddLeadingZero method

          return (k><0xF)?"0" + Integer.toHexString(k):Integer.toHexString(k);

          there is no expression in java as k>< did you mean k> or k< or k!= please specify that too...


          Please help,

          Thanks
          • 2. Re: JNDI, Active Directory and objectGUID's
            800477
            Looks like either a typo in my code, or something in the web page formatting that screwed things up.

            The AddLeading Zero function is simply:
            static String AddLeadingZero(int k) {
                return (k<0xF)?"0" + Integer.toHexString(k):Integer.toHexString(k);
            }
            If retrieving an object by the string form of its guid,because the LDAP name parser doesn't like <> characters, you must retrieve the object directly. For example:
            LdapContext ctx = new InitialLdapContext(env,null);
            String strGUID = "<GUID=7c448a67-923b-48ee-b21a-3451f2f758ca>";
            Attributes attr = ctx.getAttributes(strGUID);
            If retrieving the GUID using the binary form then you can issue an LDAP search with the binary form of the GUID (remember to escape correctly).
            SearchControls searchCtls = new SearchControls();
            searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            String returnedAtts[] = {"sn","givenName","mail"};
            searchCtls.setReturningAttributes(returnedAtts);
            String searchFilter = "(objectGUID=\\67\\8a\\44\\7c\\3b\\92\\ee\\48\\b2\\1a\\34\\51\\f2\\f7\\58\\ca)";
            String searchBase = "dc=antipodes,dc=com";
            Naming Enumeration answer = ctx.search(searchBase,searchFilter,searchCtls);
            • 3. Re: JNDI, Active Directory and objectGUID's
              843793
              Thats great it finally worked.:). I am attaching a peice of code for those who want to get the Escaped form the string. The input String is of the form

              "6448d4ff-813f-432b-a0be-e2504173f0b6" and output will be the escaped string that can be used in the search filters.
              i.e \ff\d4\48\64\3f\81\2b\43\a0\be\e2\50\41\73\f0\b6
                   public static String objectGUID(String s){
                        StringBuffer sb = new StringBuffer();
                        StringTokenizer st = new StringTokenizer(s,"-");
                        String temp,s1,s2,s3,s4,s5;
                        temp=s1=s2=s3=s4=s5=null;
                        int i=1;
                        while(st.hasMoreTokens()){
                            temp = st.nextToken();
                            switch  (i){ 
                                 case 1 :
                                      s1= temp;
                                      break;
                                 case 2 :     
                                      s2= temp;
                                      break;
                                 case 3 :
                                      s3= temp;
                                      break;
                                 case 4 :
                                      s4= temp;
                                      break;
                                 case 5 :
                                      s5= temp;
                                      break;
                                 default :
                                      break;
                            }
                             i++;
                        }
                        
                        sb.append("\\" + s1.substring(6,8));
                        sb.append("\\" + s1.substring(4,6));
                        sb.append("\\" + s1.substring(2,4));
                        sb.append("\\" + s1.substring(0,2));
                        
                        sb.append("\\" + s2.substring(2,4));
                        sb.append("\\" + s2.substring(0,2));
                        
                        sb.append("\\" + s3.substring(2,4));
                        sb.append("\\" + s3.substring(0,2));
                        
                        sb.append("\\" + s4.substring(0,2));
                        sb.append("\\" + s4.substring(2,4));
                        
                        
                        sb.append("\\" + s5.substring(0,2));
                        sb.append("\\" + s5.substring(2,4));
                        sb.append("\\" + s5.substring(4,6));
                        sb.append("\\" + s5.substring(6,8));
                        sb.append("\\" + s5.substring(8,10));
                        sb.append("\\" + s5.substring(10,12));
                        
                        return sb.toString();
                   }
              Thanks Steven.
              • 4. Re: JNDI, Active Directory and objectGUID's
                843793
                In your example you call indexes 14 and 15 of the byteArray in order to build out the entires GUID string. When i tried this against my environment, I only have 13 14 items in the byte array so I had to comment out those last lines.

                I also had to change how you built the byteArray becuase I was getting class cast exceptions from your example.

                Here is how i am building the byteArray.
                String guid = (String)attrs.get("objectGUID").get();
                                              byte[] GUID = guid.getBytes();
                I had to go this route becuase the .get("objectGUID") calls returns a string in my case, so the initial example of casting it as a byte[] was blowing up.


                With this in place, I was able to get a string and byte version of the GUID. When I turn around and try to query by the GUID (either as a String or a Byte version) I get no results. What I am thinking is that I am not converting the GUID into a valid string. Is there a way to check this, or can I send you some more examples and have you look at it?

                I feel like I am finally really close here, but am just missing a little detail.
                • 5. Re: JNDI, Active Directory and objectGUID's
                  800477
                  There must be a problem in your code as the GUID is always a fixed length.
                  Don't forget to request that the objectGUID attribute is to be returned as binary data rather than a string by specifiying
                  env.put("java.naming.ldap.attributes.binary","objectGUID");
                  When performing a search using a string format of the GUID, don't forget to escape the \ characters.
                  • 6. Re: JNDI, Active Directory and objectGUID's
                    843793
                    small bug here:

                    replace:
                    return (k<0xF)?"0" + Integer.toHexString(k):Integer.toHexString(k);

                    with:

                    return (k <= 0xF)?"0" + Integer.toHexString(k):Integer.toHexString(k);
                    • 7. Re: JNDI, Active Directory and objectGUID's
                      843793
                      failing to specify the attribute type as binary by the following code

                      env.put("java.naming.ldap.attributes.binary","objectGUID");

                      would result in java.lang.ClassCastException if you try to typecaste it into a byte[].

                      To bypass this if you typecast it into String this would corrupt the
                      the binary content of the objectGUID.



                      • 8. Re: JNDI, Active Directory and objectGUID's
                        843793
                        in steven's code... he converts the byte array to both a string hex format (strGUID) and string byte format (byteGUID).

                        How do I go about converting the string hex format to the byte format or byte array?

                        strGUID --> byteGUID
                        strGUID --> GUID
                        • 9. Re: JNDI, Active Directory and objectGUID's
                          843793
                          never mind....
                          http://forum.java.sun.com/thread.jspa?threadID=546486&messageID=2661501
                          • 10. Re: JNDI, Active Directory and objectGUID's
                            843793
                            >
                            If retrieving an object by the string form of its guid,because the LDAP name parser doesn't like <> characters, you must retrieve the object directly. For example:
                            LdapContext ctx = new InitialLdapContext(env,null);
                            String strGUID = "<GUID=7c448a67-923b-48ee-b21a-3451f2f758ca>";
                            Attributes attr = ctx.getAttributes(strGUID);
                            Would this work if I use along with DirSync for each object obtained from Active Directory? would I have to specify any special search control?
                            • 11. Re: JNDI, Active Directory and objectGUID's
                              800479
                              Thanks guys.
                              This was too appreciate to me!