Querying custom LDAP property via System.DirectoryServices in C#? - c#

I have a custom LDAP schema installed on my OpenLDAP server which is as follows:
attributeType ( 999.0.01
NAME 'picturePath'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024}
)
objectClass ( 999.1.01
NAME 'indieStackTeam'
DESC 'Team definition for IndieStack'
SUP groupOfUniqueNames
STRUCTURAL
MAY ( picturePath )
)
In my ASP.NET MVC 2 application, I'm querying for the picturePath property like so (and it is confirmed that picturePath exists in the list of keys):
this.Picture = properties["picturePath"].Value as string;
When I attempt to do this under .NET 3.5 I get the following exception:
[COMException (0x8000500c): Unknown error (0x8000500c)]
System.DirectoryServices.PropertyValueCollection.PopulateList() +347013
System.DirectoryServices.PropertyValueCollection..ctor(DirectoryEntry entry, String propertyName) +49
System.DirectoryServices.PropertyCollection.get_Item(String propertyName) +150
However, when the same code runs under Mono (on the same server as OpenLDAP) it works perfectly fine. Clients such as LDAPAdmin can also read the picturePath property correctly.
More so, it's only when I go to read the value that it fails; I can see the property is there in the keys list, I just can't access it.
Unfortunately unknown error doesn't tell me a lot about what's going wrong, but I'm finding the .NET implementation of System.DirectoryServices is very flaky (you get the same unknown error if you connect to the LDAP server using lowercase in 'DC=').
Has anyone had this problem before and if so, how is it solved?

Two things you should check:
1) does that particular user object indeed have a value in picturePath? You might want to check for existance of the property before accessing it:
if(properties.Contains("picturePath") && properties["picturePath"].Count > 0)
{
....
}
2) If I remember correctly, to get access to custom attributes, you should explicitly refresh the cache for a user object before doing anything:
DirectoryEntry de = ......; // find / assign that DirectoryEntry somehow
de.RefreshCache(); // to load all properties from the directory
or:
de.RefreshCache(new string[] { "picturePath" }); // to just load the "picturePath" attribute
Also: the classes in System.DirectoryServices are really mostly geared towards being used against Active Directory - there might be "surprises" or subtle incompatibilities when used against some other LDAP server - like OpenLDAP.

It seems that the .NET LDAP client expects a correctly formed OID for attribute types and object classes.
You'll note that I was using OIDs of the form 999.X.YY, which while they might be syntactically correct, aren't usually encountered in the real world. My guess is the LDAP client parses OIDs and since these don't conform to what is expected, it throws an error.
I changed the OIDs to 1.3.6.1.4.1.40000.1.3.1 and 1.3.6.1.4.1.40000.1.4.1 respectively (I've also applied for a PEN, which will give me an assigned number instead of '40000'), refreshed the schema in the server and recreated the entries and the LDAP client now correctly reads the custom attributes.

Related

Issue with Profile in ASP.NET Web Forms application

I am trying to use Profile in an ASP.NET web Forms application and I have run into a maddening problem I can't resolve.
I know that if I have profiles set up (and include the System.Web.Profile namespace) then I should be able to access profile information on the current user by using this code:
Profile.LastName=... \\ assumes I have a defined profile property for last name, of course
But IntelliSense doesn't show 'Profile' as an option, and if I try to use it anyway, I get the error Profile doesn't exist in the current context. So, instead, I am trying to do it this way:
In my .aspx code-behind file, I have the following code:
ProfileCommon profile = HttpContext.Current.Profile as ProfileCommon;
profile.LastName = last;
profile.FirstName = first;
profile.BestContactEmail = email;
profile.Save ( );
The first line runs properly, and the 'profile' variable shows the defined properties, but trying to access those properties throws the error that profile is null.
I don't understand what I'm missing here. Why is this not working?
So, after fiddling around with this for a full day, I found a way to make it work, because for some reason, retrieving the member's profile using var profile = HttpContext.Current.Profile; doesn't work.
In light of this, I found that this works, at least to the extent of creating a profile and storing its data in the database:
var profile = ProfileBase.Create(uname.Value);
profile.SetPropertyValue ( "LastName", last_name.Value );
profile.SetPropertyValue ( "FirstName", first_name.Value );
profile.Save ( );
Weird, but it works.

Renaming an existing entry in the directory gives an error saying "Naming Violation"

I'm using Novell in asp.net core 2.2 application to interact with AD. Following functions are working as expected.
Getting all users, Getting users from specific OU
Create an User
Update an User
Reset password and etc
But when i try to move the entry to new container it gives following exception
Naming Violation
((Novell.Directory.Ldap.LdapException)e).LdapErrorMessage : "00000057: LdapErr: DSID-0C090E72, comment: Error in attribute conversion operation, data 0, v4563"
Here is the code block i'm using.
var dn = $"CN={user.FirstName} {user.LastName},{this._ldapSettings.ContainerName}";
//dn => CN=arshath shameer,CN=Users,DC=wxyzdev,DC=xyzdev,DC=ca
var newRDn = $"CN={user.FirstName} {user.LastName},OU=DeletedUsers,DC=wxyzdev,DC=xyzdev,DC=ca";
// newRDn => CN=arshath shameer,OU=DeletedUsers,DC=wxyzdev,DC=xyzdev,DC=ca
using (var ldapConnection = this.GetConnection())
{
//ldapConnection.Delete(dn);
ldapConnection.Rename(dn, newRDn, dn, true);
}
I'm following this link.
There are 2 issues to fix :
RDN means relative DN : the part in the DN that actually makes an
entry distinguishable from others in the same container, for example :
CN=arshath shameer in CN=arshath
shameer,CN=Users,DC=wxyzdev,DC=xyzdev,DC=ca. In your case, since you don't want to rename but to move an entry, it doesn't change :
var newRDn = $"CN={user.FirstName} {user.LastName}";
When moving an entry - contrary to renaming - the RDN stays the same,
but the parentDN changes :
var parentDN = "OU=DeletedUsers,DC=wxyzdev,DC=xyzdev,DC=ca";
Now let's move the entry :
ldapConnection.Rename(dn, newRDN, parentDN, true);
You may also need to check whether {this._ldapSettings.ContainerName} is replaced with CN=Users,DC=wxyzdev,DC=xyzdev,DC=ca to ensure dn variable is correctly set.
I had encountered this issue. Having come across this thread from google, it was not clear to use "CN=arshath shameer".
Please use "CN=arshath shameer" instead of "CN=arshath
shameer,CN=Users,DC=wxyzdev,DC=xyzdev,DC=ca" in the newRDN parameter.
Thanx.
FRS.

'Access Denied' when attempting DirSync with S.DS

I am trying to set up a DirSync control. Previously I have [successfully] used the methods found in the System.DirectoryServices.Protocols, but I found that the results it returned were only partial objects - I wasn't able to have it return the homeDrive attribute from a user even if I defined it in the SearchRequest's Attributes property.
Thus, I'm attempting to set up DirSync following some of the documentation and examples using System.DirectoryServices instead. I was successful in connecting to my test server (only accessible by IP), and I was successful in targeting just one OU and searching for a user, as such:
byte[] cookie = null;
root = new DirectoryEntry(
"LDAP://[MyIPHere]/OU=test ou,DC=company,DC=com", "username", "password");
//Section A - Use this section for a regular search
DirectorySearcher src = new DirectorySearcher(root);
src.SearchScope = SearchScope.Subtree;
src.Filter = "(&(objectClass=user)(sAMAccountName=myuserhere)";
//Section B - Use this section for a DirSync
//src.DirectorySynchronization = new DirectorySynchronization(
DirectorySynchronizationOptions.IncrementalValues, cookie);
//src.Filter = "(&(objectCategory=person)(objectClass=user))";
//Execute the code whichever section is used
SearchResultCollection result = src.FindAll();
int count = result.Count;
Console.WriteLine(count.ToString());
foreach (SearchResult res in result)
{
//do things
}
However, when I try to use section B instead of section A, I get an error on the line where I'm setting the int count. (I have tried passing no parameters to the constructor of src.DirectorySynchronization as in the example, same result):
COMException was unhandled
Access is denied.
I only get the error when I try to access the properties of the result object, or try to iterate. If I set a breakpoint on the int count line and look at the result object, I see the following in the value column of the result's Count:
'result.Count' threw an exception of type
'System.Runtime.InteropServices.COMException'
I have ensured that my account has the Replicating Directory Changes security access both for the specified OU and the test domain at large (and all other security access possible). I have also tried using a separate domain admin account.
I have the same issue if I try running this on our production domain, passing no credentials when constructing the DirectoryEntry object.
Considering that I can successfully retrieve other search results, what is it about this DirectorySynchronization that is causing the access issues, and why isn't it happening when I call src.FindAll()?
(I'm open to other options, but I'd like to avoid the USNChanged tracking method for now due to it returning the full objects back, and requiring additional coding on my end.)
The base of a DirSync search must be the root of a directory partition, i.e "DC=company,DC=com" in your case.
See http://msdn.microsoft.com/en-us/library/ms677626(v=vs.85).aspx
"Base of the search" in the table.
Some more good C# example for DirSync:
http://msdn.microsoft.com/en-us/magazine/cc188700.aspx#S1
See section "Finding Your Way with DirectorySearcher".
If you want to track changes only on a OU/container, yes, you will have to use USNChange.

BizTalk Dynamic Disassembler Problems - The body part is NULL

I started with the solution here http://social.technet.microsoft.com/wiki/contents/articles/20547.biztalk-server-dynamic-schema-resolver-real-scenario.aspx
which matches my scenario perfectly except for the send port, but that isn't necessary. I need the receive port to choose the file and apply a schema to disassemble. From their the orchestration does the mapping, some of it custom, etc.
I've done everything in the tutorial but I keep getting the following error.
"There was a failure executing the receive pipeline... The body part is NULL"
The things I don't get from the tutorial but don't believe they should be an issue are:
I created a new solution and project to make the custompipeline component (reference figure 19) and thus the dll file. Meaning it is on it's own namespace. However, it looks like from the tutorial they created the project within the main biztalk solution (ie the one with the pipeline and the orchestration) and thus the namespace has "TechNetWiki.SchemaResolver." in it. Should I make the custompipeline component have the namespace of my main solution? I'm assuming this shouldn't matter because I should be able to use this component in other solutions as it is meant to be generic to the business rules that are associated with the biztalk application.
The other piece I don't have is Figure 15 under the "THEN Action" they have it equal the destination schema they would like to disassemble to but then they put #Src1 at the end of "http://TechNetWiki.SchemaResolver.Schemas.SRC1_FF#Src1". What is the #Src1 for?
In the sample you've linked to, the probe method of the pipeline component is pushing the first 4 characters from the filename into a typed message that is then passed into the rules engine. Its those 4 characters that match the "SRC1" in the example.
string srcFileName = pInMsg.Context.Read("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties This link is external to TechNet Wiki. It will open in a new window. ").ToString();
srcFileName = Path.GetFileName(srcFileName);
//Substring the first four digits to take source code to use to call BRE API
string customerCode = srcFileName.Substring(0, 4);
//create an instance of the XML object
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(string.Format(#"<ns0:Root xmlns:ns0='http://TechNetWiki.SchemaResolver.Schemas.SchemaResolverBRE This link is external to TechNet Wiki. It will open in a new window. '>
<SrcCode>{0}</SrcCode>
<MessageType></MessageType>
</ns0:Root>", customerCode));
//retreive source code in case in our cache dictionary
if (cachedSources.ContainsKey(customerCode))
{
messageType = cachedSources[customerCode];
}
else
{
TypedXmlDocument typedXmlDocument = new TypedXmlDocument("TechNetWiki.SchemaResolver.Schemas.SchemaResolverBRE", xmlDoc);
Microsoft.RuleEngine.Policy policy = new Microsoft.RuleEngine.Policy("SchemaResolverPolicy");
policy.Execute(typedXmlDocument);
So the matching rule is based on the 1st 4 characters of the filename. If one isn't matched, the probe returns a false - i.e. unrecognised.
The final part is that the message type is pushed into the returned message - this is made up of the namespace and the root schema node with a # separator - so your #src1 is the root node.
You need to implement IProbeMessage near to class
I forgot to add IProbeMessage in the code of article. It is updated now.
but it is there in sample source code
Src1 is the the root node name of schema. I mentioned that in article that message type is TargetNamespace#Root
I recommend to download the sample code
I hope this will help you

Active Directory custom attribute can not be seen by it's owner on some computers

A added a custom attribute to the Active Directory Schema. On one machine when I try to query the attribute I get errors back from my code.
Here is the C# version to test this out.
static class Program
{
static void Main()
{
Console.ReadLine();
DirectoryEntry directoryEntry = (DirectoryEntry)UserPrincipal.Current.GetUnderlyingObject();
//Execption on this line
var allowedDatabases = directoryEntry.Properties["vwDBAccess"];
foreach (var record in allowedDatabases.OfType<String>())
{
Console.WriteLine(record);
}
Console.ReadLine();
}
}
System.Runtime.InteropServices.COMException was unhandled
Message=Unknown error (0x8000500c)
Source=System.DirectoryServices
ErrorCode=-2147463156
StackTrace:
at System.DirectoryServices.PropertyValueCollection.PopulateList()
at System.DirectoryServices.PropertyValueCollection..ctor(DirectoryEntry entry, String propertyName)
at System.DirectoryServices.PropertyCollection.get_Item(String propertyName)
at Sandbox_Console.Program.Main() in C:\Users\srchamberlain.VW\documents\visual studio 2010\Projects\Sandbox Console\Sandbox Console\Program.cs:line 16
InnerException:
The Error Code 0x8000500c represents E_ADS_CANT_CONVERT_DATATYPE. This only happens on one machine. I have 3 other computers (all part of the same domain as the first computer) and those behave correctly when running the exact same code for the exact same user and give the the content of the attribute. Also if I run as a different user, on the same box, but query the bad user's attributes I can pull up the information correctly when connecting as another user.
I have tried refreshing the schema on the box using the technique from this KB article but the issue is still happening.
What is going wrong on this one computer?
Clarification:
vwDBAccess is a multivalued string, so when it works directoryEntry.Properties["vwDBAccess"] return a string with there is one item, sting[] when there is more than one, and null when there are no items. This account has 3 items set. When I run as a different user and query the bad user I correctly get string[3] returned.
Typically if something is only happening on one machine in a network then it boils down to service pack and update levels of the OS or interaction with other software on the system (A/V is the worst offender).
The first thing I would do is look at the SP and updates applied to the working machines, then compare that to the non-working one. You should see one of two situations:
If the working machines are more up to date, then apply whatever updates are necessary to the non-working machine.
If the working machines are less up to date, then update one and see if it starts failing. If that's the case, you might need to contact MS.
My gut says that the non-working machine is simply out of date.

Categories

Resources