locked
DirectoryServices and getting/setting custom Property RRS feed

  • Question

  • User1603054497 posted
    I am trying to get/set a custom property in active directory, but nothing I try is working.  I have tried many ways, this is the way I am trying right now:

    DirectoryEntry oDE = new DirectoryEntry("LDAP://CN=bjones,OU=ActivCommunity Users,DC=activplant,DC=community", sLookupDomainName + "\\" + sLookupUsername, sLookupPassword);
    Object oNative = oDE.NativeObject;
    DirectorySearcher oSearch = new DirectorySearcher(oDE);
    oSearch.PropertiesToLoad.Add("apSFDCContactID");
    SearchResult oResult = oSearch.FindOne();
    if (oResult.GetDirectoryEntry().Properties.Contains("apSFDCContactID"))   <<ERROR
    {
        //do stuff
    }

    And it throws this: {"Unknown error (0x8000500c)"}

    When I try to set the value for this property, that fails with a similar "unknown exception" error.  My c# web app (2.0) uses impersonation setup in the web.config file.

    Does anyone have any thoughts on this?

    Thanks
    Thursday, July 6, 2006 11:06 AM

All replies

  • User1354132231 posted
    Well, technically there is nothing different about the custom versus the OOB attributes.  Assuming you correctly updated the schema, they work just like any other attribute.

    Using your 'oDE' variable, it should be as simple as this:

    if (oDE.Properties.Contains("attribute"))
    {
        object val = oDE.Properties["attribute"].Value;
    }


    If you are not seeing this attribute on the object, it can be for two reasons:

    1.  It is not set on the object (i.e. it has no value).
    2.  You did not add it to the schema correctly.

    To check #2, do this:

    oDE.RefreshCache(new string[] {"allowedAttributes", "allowedAttributesEffective});
    if (!oDE.Properties["allowedAttributes"].Contains("yourcustomone"))
    {
        Console.WriteLine("I did not associate the attribute to my schema correctly");
    }
    else
    {
        if (!oDE.Properties["allowedAttributesEffective"].Contains("yourcustomone"))
        {
           Console.WriteLine("I have it in my schema, but forgot to give permission to update it and/or read it!");
        }
    }


    On a side note: why are you searching here?  Did you know that it was not necessary?  You have the DirectoryEntry already.  It doesn't make sense to search (especially when using no filter btw) and return the same object as a SearchResult.  It especially doesn't make sense to then return the DirectoryEntry again using GetDirectoryEntry (DirectoryEntry -> SearchResult -> DirectoryEntry).  Each one in the chain is the same directory object.  You don't want to do this...

    Thursday, July 6, 2006 11:49 AM
  • User1603054497 posted
    Hi Dunnry,
    Thanks for the things to try.  I know I didn't need the searchresult object, but I had tried everything else... thought I'd see what happens.  I tried your test code to see if the property exists, and indeed it does exist.  When stepping through the code it never hits any of console.writeline lines.  Here is what my code looks like right now.

    DirectoryEntry oDE = new DirectoryEntry("LDAP://" + sDN, sLookupDomainName + "\\" + sLookupUsername, sLookupPassword, AuthenticationTypes.Secure);
    Object oNative = oDE.NativeObject;
    oDE.RefreshCache(new string[] { "apSFDCContactID" });
    oDE.Properties["apSFDCContactID"].Add("test");
    oDE.CommitChanges();

    It throws an exception "The specified directory service attribute or value does not exist" on the "oDE.CommitChanges();" line.  I'm sure the property does exist, because I can read the value for users that have it set.  I have also tried this to set the value:

    oDE.Properties["apSFDCContactID"].Value = "test1";

    But it throws the same error.
    Any thoughts?

    Thanks
    Thursday, July 6, 2006 1:56 PM
  • User1354132231 posted
    I would test trying to set this with LDP.exe to remove any issue with ADSI or .NET.  I am beginning to suspect it is a permission issue of some sort.  What you can do is this:

    1. Open LDP.exe (from ADAM install or AdminPak.msi).  File > Connect, type name of domain
    2. Type CTRL-B and bind using the user's credentials you have in your code.  Make sure you see successful auth in window.
    3. Type CTRL-S and bring up search window.  Put filter in (sAMAccountName=username) and make sure root DN is of your partition (DC=blah...).  Scope should be Subtree.  Click Options button and add your attribute at the end of the list to be returned.
    4. Run the search and see if you got anything.  Copy the DN of the user to the clipboard.
    5. Type CTRL-M and paste the DN into the Dn: textbox.
    6. Put the name of the attribute and value into the two boxes and select either Add or Replace (depending if the object already had a value) and then click the Enter button.  It should be in the listbox now.
    7. Click Run and you should see either success or some error message.  What does it say?

    Report back what you get and we can start to narrow the problem down.
    Thursday, July 6, 2006 3:31 PM
  • User1603054497 posted
    Hi Dunnry,
    Here is what I get when I run that "replace" command:

    -----------
    ***Call Modify...
    ldap_modify_s(ld, 'CN=bjones,OU=ActivCommunity Users,DC=activplant,DC=community',[1] attrs);
    Modified "CN=bjones,OU=ActivCommunity Users,DC=activplant,DC=community".
    -----------

    It looks like it worked.  :(  This is very strange.  I am suspecting it is a problem with my code somewhere.  I can set alot of other properties for this user, like the name, sn... etc, I just can't set any of the custom properties that I added to the schema.

    Any thoughts?
    Thanks
    Friday, July 7, 2006 1:10 PM
  • User1354132231 posted
    Interesting... what kind of attribute is this?  Single- or multi-valued and syntax type?
    Tuesday, July 11, 2006 12:44 PM
  • User1603054497 posted
    Hi Dunnry,
    I have some updates with this problem.  I got frustrated trying to get this to work in a webapp, so I simplified it as much as possible.  I took the code and put it into a winforms app, just to eliminate the possibility of there being an impersonation problem or something.  So here is the code:

    string sProperty = txtProperty.Text.Trim();
                    string sValue = txtValue.Text.Trim();
                    string sDN = "CN=bjones,OU=ActivCommunity Users,DC=activplant,DC=community";

                    DirectoryEntry oDE = new DirectoryEntry("LDAP://" + sDN);
                    oDE.RefreshCache(new string[] { sProperty });

                    if (oDE.Properties.Contains("apSFDCContactID"))
                    {
                        oDE.Properties[sProperty][0] = sValue;
                    }
                    else
                    {
                        oDE.Properties[sProperty].Add(sValue);
                    }
                    oDE.CommitChanges();

    Ok, so here are the results.  If I run this app as the domain administrator it works fine everytime.  When I run it as my other user (Username: adlookup), which is in a different domain (but the 2 domains have a 1 way trust between them) it never works.

    When run I run it as "adlookup" and the property doesn't already have a value set, I get this exception:
    The specified directory service attribute or value does not exist. (Exception from HRESULT: 0x8007200A)

    When I run it as "adlookup" and the property does already have a value set I get this exception:
    Unknown error (0x8000500c)

    The property is just a unicode string.

    I am very confused - do you have any other suggestions?

    Thanks
    Thursday, July 13, 2006 10:11 AM
  • User1354132231 posted
    If you logged into ldp.exe (CTRL-B) as the 'adlookup' user, then I think it is a caching issue with the schema.  What is the syntax of this attribute?

    The last error indicates it does not know how to convert to the data type.  This usually indicates a schema problem.  Are you using .NET 1.1 or 2.0?  Try this and tell me what you get (with the adlookup user):

    string dn = "CN=bjones,OU=ActivCommunity Users,DC=activplant,DC=community";
    string attrib = "apSFDCContactID";

    DirectoryEntry entry = new DirectoryEntry(
        String.Format("LDAP://{0}", dn),
        @"domain\adlookup",
        "password",
        AuthenticationTypes.Secure
        );

    using (entry)
    {
        DirectorySearcher ds = new DirectorySearcher(
           entry,
           String.Format("({0}=*)", attrib);
           new string[] { attrib },
           SearchScope.Base
           );
       
        SearchResult sr = ds.FindOne();

        if (sr == null)
        {
           //didn't have the attribute
           Console.WriteLine(
               "{0} does not contain the {1} attribute",
               entry.Name,
               attrib
               );
        }
        else
        {
           try
           {
               //had the attribute and we get an error
               //or a type to view.
               object val = sr.Properties[attrib][0];
               Console.WriteLine(val.GetType());
           }
           catch (Exception ex)
           {
              Console.WriteLine(ex.ToString());
           }
        }      
    }


    Thursday, July 13, 2006 12:48 PM
  • User1603054497 posted
    I tried the above code and it throws this exception when running as adlookup:
    "CN=bjones does not contain the apSFDCContactID attribute"

    When I try to run the code as my domain admin account (which worked with the old code) it throws the same exception.  Do I need to set the "PropertiesToLoad" property when using the searcher?  Or do I need to use the "RefreshCache" for the directoryentry?

    Thanks
    Thursday, July 13, 2006 1:33 PM
  • User1354132231 posted
    I am setting the PropertiesToLoad with the constructor on the DirectorySearcher.  It is actually not really that critical here since the filter has eliminated it first.

    Has the bjones account had the attribute set?  I meant for this to be tested with the account that actually has it set.  I want to see what it says when you try to access it from the searcher.  Make sure the attribute has a value and re-run this.

    Thursday, July 13, 2006 2:22 PM
  • User1603054497 posted
    Sorry about that.  I set the value and ran the program again as "adlookup".  The result I got is: "System.Byte[]".

    I hope this helps.

    Thanks

    Thursday, July 13, 2006 3:22 PM
  • User1354132231 posted
    Yep... that's what I thought.  I take it you are on 2.0.  What is the syntax type!?
    Thursday, July 13, 2006 3:31 PM
  • User1603054497 posted
    I am on 2.0.  The syntax type is unicode string.  I set the property value for it use adsiedit, if that makes any difference.
    Thursday, July 13, 2006 3:34 PM
  • User1354132231 posted
    For some reason, it is not marshaling the type correctly.  This usually means the schema is not getting cached or is hosed already and cached.

    Check your schema and confirm that it is this type exactly:

    String (Unicode)

    This one is fairly common, but if you picked another syntax it might cause data mapping issues.  You don't own the book do you - I could point you to a chapter that would help clarify the schema and caching.  It's ok if you don't...
    Thursday, July 13, 2006 3:50 PM
  • User1354132231 posted
    I think I know what the issue is and it is fairly esoteric.  It has to do with the fact that your user that is trying to set it is not in the same domain.  I am willing to bet that *any* user with permission to set this attribute that resides in the same domain as where this schema resides will work.  Further, it will fail with any user outside the domain in your current setting.  You might try this for grins and giggles.

    The issue has to do with the other domain not being able to refresh the cache from the DC syntax alone.  If you change the syntax for your adsPath to bjones user to this, it should work:

    "LDAP://activplant.community/CN=bjones,OU=ActivCommunity Users,DC=activplant,DC=community"

    This will allow the other domain's security context to download and refresh the schema.  I personally feel this is a bug... but hey.  Try my last sample again, but add in the activeplant portion and tell me what happens now.
    Thursday, July 13, 2006 4:00 PM
  • User1603054497 posted
    In adsiedit it does say the unicode string, but I am not familiar with this caching you mentioned.  I unfortunately don't own the book, but I'm beginning to think I might need to.  [:)]  Is this caching issue something that is server/AD related, or coding related?
    Thursday, July 13, 2006 4:01 PM
  • User1354132231 posted
    If you check using the schema mmc you can see the syntax more easily.  It should read 'case-insensitive string' and say 2.5.5.12.  If that is the case, then there is no reason that ADSI should barf on it since it knows how to deal with this type and marshal to/from string.

    If you are running this code from a machine that is not in the domain where the object resides (and you are right?), then I think it is the problem I posted about last time.  The security context cannot download and cache the schema to get the type when you use the serverless binding format.  Just add the domain in there like I mentioned and it should probably start working.

    ps. That's ok (about the book). :)
    Thursday, July 13, 2006 4:10 PM
  • User1603054497 posted
    It works!!! I added in the "LDAP://activplant.community/" part to the beginning of the path and it worked.  I can't tell you how relieved I am, and there's no way I ever would have guessed to do that.  I hope other people will find this thread useful as well.  I know I couldn't find any documentation anywhere on the internet relating to active directory and user's in other domains, like in this situation.  Thanks again Dunnry, you are truly a master of active directory.  [:D]
    Friday, July 14, 2006 8:01 AM
  • User-2001589993 posted

    Hi,

    can you explain one more time about the above mentioned Unknown error? We have a problem while trying to reproduce it. It is extremely required to us because a customer has got this error at his computer but we haven’t got an access to they environment. We have the following test environment: two domain controllers. The code trying to access an Active Directory service:

    <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p> </o:p>

    string strQuery = “LDAP://AdServer:389”;

    <o:p> </o:p>

    DirectoryEntry    deRoot = new DirectoryEntry(strQuery, domainUsername, Password);

                                                    searcher.PropertiesToLoad.Add(“AMAccountName”);<o:p></o:p>

                                                    searcher.PropertiesToLoad.Add(“proxyAddresses”);<o:p></o:p>

                                                    searcher.PropertiesToLoad.Add(”mail”);

    <o:p> </o:p>

    DerectorySeacher searcher = new DirectorySearcher(deRoot);

    <o:p> </o:p>

    searcher.Filter = “(&(objectClass=user)(sAMAccountName=userName))”;

    <o:p> </o:p>

    SearchResultCollection ResultCollection = searcher.FindAll();

    <o:p> </o:p>

    DirectoryEntry UserAccountEntry = ResultCollection[0].GetDirectoryEntry();

    <o:p> </o:p>

    /* The following statement causes an error Unknown error (0x8000500c)  while processing UserAccountEntry.Properties[“proxyAddresses”]*/

    <o:p> </o:p>

    if (UserAccountEntry.Properties[“proxyAddresses”].Value != null)

    {

    ………….

    }

    <o:p> </o:p>

    I have not ever had any impression about how the customer reproduces the mentioned error at the last code line. The code works Ok at my machine. Any ideas are appreciated.

    <o:p> </o:p>

    Tanks a lot.

    Monday, July 17, 2006 10:25 AM
  • User1354132231 posted
    The error code '0x8000500C' actually means that ADSI cannot figure out how to map the type back to the schema type.  This usually indicates a problem with the schema not being cached or downloaded correctly.

    However, 'proxyAddresses' is not a custom attribute, so the solution to this thread might have no bearing on your issue.  You should probably explain in great detail your setup and how you are accessing the directory.  The key detail that initially I overlooked from the last poster was that he was accessing a different domain from the one where the code was running.  Most people would forget to mention that...

    Monday, July 17, 2006 12:29 PM