I have developed an application that talks to the Directory Server and gets user information.
This application is a generic one and can talk to Active Directory or Any other Directory Services.
In one case where i use this application to read data from Radiant One VDS, the application fails with the ERROR_MORE_DATA. Following is the code that returns this error:
try
{
using (DirectoryEntry de = new DirectoryEntry("LDAP://" + server + "/" + basedn, username, pwd,AuthenticationTypes.None))
{
using (DirectorySearcher Searcher = new DirectorySearcher(de))
{
Searcher.Filter = "(&(objectClass=user))";
Searcher.ReferralChasing = ReferralChasingOption.All;
Searcher.PropertiesToLoad.Add("cn");
Searcher.PropertiesToLoad.Add("memberof");
Searcher.PageSize = 1000;
using (SearchResultCollection allUsers = Searcher.FindAll())
{
foreach (SearchResult user in allGroups)
{
.
.
.
.
}
}
}
}
}
catch(System.Exception ex)
{
}
In the above code, Searcher.FindAll() returns ERROR_MORE_DATA. When i searched i found the this article.
But, this article talks about .NET 1.0 and my application runs with .NET 3.5
Can you anyone please help me here? Is there any way to fix this without going for DirectoryServices.Protocols?
Generally, this issue comes in the following situation:-
If one or more entries are found in Network and the buffer size is not enough to hold it.
see ERROR_MORE_DATA
You just need to specify the size of buffer.
see: specify the size of buffer in network call
Related
I wrote a small app to check AD group members. When I execute the following code on my pc, It works well, the SearchResult contains the "member" property, however when I run the same exe on the server or on an another computer the "member" property is missing. The usnchanged and usncreated will be different also. I run the exe with the same user on every pc. What can cause this?
...
using (DirectorySearcher searcher = new DirectorySearcher())
{
searcher.CacheResults = false;
searcher.Filter = "(&(objectClass=group)(cn=" + ADName + "))";
searcher.SizeLimit = int.MaxValue;
searcher.PageSize = int.MaxValue;
if (!DirectoryEntry.Exists(ADPath))
{
return null;
}
searcher.SearchRoot = new DirectoryEntry(ADPath);
using (SearchResultCollection collection = searcher.FindAll())
{
if (collection.Count == 1)
{
return collection[0];
}
}
}
...
The group membership data is not replicated to the global catalog. The query might work sometimes, if you happen to connect to the domain controller with the actual membership data. On other machines, you probably connect to other domain controllers, of different domains, where the information is not available.
You might want to connect to a domain controller in the actual domain, not to the global catalog.
I'm researching what I need to build an Active Directory search tool which ultimately will be used in a C# ASP.NET web app.
I know very little about AD (and don't particularly want to know any more than necessary) so I've asked our tech ops to set up a dummy instance on a server. This they've done giving it the domain dummy.local
I now need to work out my LDAP connection string. Here I'm completely stuck. I'm using a user account which is a member of Domain Admins. After loads of hunting round the web I've tried all sorts of things to work out the various components of the LDAP connection string. For example, if I run the following in cmd.exe for that domain admin user on the server...
dsquery user -samid "username" | dsget user -memberof -expand
...this gives me the following information:
"CN=Domain Admins,CN=Users,DC=dummy,DC=local"
"CN=Remote Desktop Users,CN=Builtin,DC=dummy,DC=local"
"CN=Users,CN=Builtin,DC=dummy,DC=local"
"CN=Administrators,CN=Builtin,DC=dummy,DC=local"
"CN=Domain Users,CN=Users,DC=dummy,DC=local"
"CN=Denied RODC Password Replication Group,CN=Users,DC=dummy,DC=local"
I've also run the following in a C# console app...
using (var context = new PrincipalContext(ContextType.Domain))
using (var comp = ComputerPrincipal.FindByIdentity(context, Environment.MachineName))
{
Console.WriteLine(String.Join(",", comp.DistinguishedName.Split(',').SkipWhile(s => !s.StartsWith("OU=")).ToArray()));
}
...and this gives me the following:
OU=Domain Controllers,DC=dummy,DC=local
I thought I therefore had all the properties I needed to build my LDAP connection string. I ended up with this:
LDAP://COMSECWEBDEV.dummy.local/ou=Domain Controllers,/cn=Domain Admins,/cn=Users,/dc=dummy,/dc=local
I've tried using this connection string, with the username and password of the domain admin which I know is correct, but everything I try always gives me the same error:
System.Runtime.InteropServices.COMException (0x80005000): Unknown error (0x80005000)
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.DirectorySearcher.FindAll(Boolean findMoreThanOne)
at System.DirectoryServices.DirectorySearcher.FindOne()
Since this error gives me no detail I have no idea what I'm doing wrong. I'm sure I'm just not getting the connection string right, but I've no idea how to work out the correct string.
For completeness, here is the console code which I'm testing with:
static void Main(string[] args)
{
var connString = ConfigurationSettings.AppSettings["lc"];
var username = ConfigurationSettings.AppSettings["lu"];
var password = ConfigurationSettings.AppSettings["lpw"];
using (DirectoryEntry de = new DirectoryEntry(connString, username, password))
{
DirectorySearcher search = new DirectorySearcher(de);
search.PageSize = 1001;// To Pull up more than 100 records.
DirectorySearcher directorySearcher = new DirectorySearcher(de);
directorySearcher.Filter = string.Format("(&(objectClass=user)(objectCategory=user) (sAMAccountName={0}))", username);
directorySearcher.PropertiesToLoad.Add("msRTCSIP-PrimaryUserAddress");
try
{
var result = directorySearcher.FindOne();
var found = false;
if (result != null)
{
if (result.Properties["msRTCSIP-PrimaryUserAddress"] != null)
{
found = true;
Console.WriteLine("Found: " + result.Properties["msRTCSIP-PrimaryUserAddress"][0]);
}
}
if (!found)
{
Console.WriteLine("Sad face");
}
}
catch (Exception x)
{
Console.WriteLine(x.Message);
}
Console.WriteLine("------------");
}
}
I was trying to figure out how to properly format a LDAP connection string last week, and found this entry over on serverfault:
How can I figure out my LDAP connection string?
I noticed you had a "/" between each OU or DC entry - I didn't include those in mine, and I don't see them included in the above example either.
I'm far from an expert (obviously) but just figured I would throw it out there.
I want to write and deploy some software without the need to install Oracle drivers on the users' workstations, rather, I would like to copy everything needed to a network location, and for the users to launch from there.
I have successfully established an OracleConnection by deploying the following to the application's folder. (I took these from instantclient-basiclite-nt-11.2.0.3.0 provided by Oracle )
oci.dll
ociw32.dll
orannzsbb11.dll
oraocci11.dll
oraociicus11.dll
I have found this works with an LDAP Query to establish the DataSource= part of the connection string. The code I use is as follows - I hope this is useful to someone.
List<string> LDAPServers
string OracleInstance
private string GetOracleNetDescriptor()
{
string result = null;
foreach (string ldapserver in LDAPServers)
{
try
{
DirectoryEntry de = new DirectoryEntry(String.Format("LDAP://{0}", ldapserver), null, null, AuthenticationTypes.Anonymous);
DirectorySearcher ds = new DirectorySearcher(de);
ds.PropertiesToLoad.Add("orclnetdescstring");
ds.SearchScope = SearchScope.Subtree;
ds.Filter = String.Format("(CN={0})", OracleInstance);
SearchResult sr = ds.FindOne();
if (sr != null)
{
result = Encoding.Default.GetString(sr.Properties["orclnetdescstring"][0] as byte[]);
break;
}
}
catch
{
result = null;
}
}
if (result == null)
throw new Exception(String.Format("Failed To Identify Oracle Instance '{0}' From LDAP", OracleInstance));
else
return result;
}
So far so good, however ...
The solution is currently dependent on a reference to the deprecated System.Data.OracleClient which I really need/want to avoid
I would like to get access to some advanced features, such as Entity Framework.
Can the panel advise ?
I would also appreciate any comments on the ldap code above as this is all work-in-progress.
Had the same problem with the deprecated provider. I've made good expiriences with Devart dotConnect for Oracle:
http://www.devart.com/dotconnect/oracle/
It is not free of charge (and I'm not related to this company nor would I benefit in any kind).
It works with ADO.Net's DataTable and DataSet. As some years passed since my last usage I don't know how well it integrates with EF.
I am trying to get the list of local users of a computer using the following code.
internal void GetUsers()
{
try
{
List<string> adUsers = new List<string>();
DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" + Environment.MachineName);
foreach (DirectoryEntry child in directoryEntry.Children)
{
if (child.SchemaClassName.Equals("User", StringComparison.OrdinalIgnoreCase))
{
adUsers.Add(child.Name);
}
}
}
catch (Exception ex)
{
//Exception
}
}
This code works fine in my computer. However, when I tested it on a few other computers, the following system users were included in the list:
ASPNET,
HelpAssistant
Could some one throw some light on how I can get rid of these system users and get only users who actually log in, ie, normal users.
Thanks,
Ram
Not an answer as such, but some suggestions that might help.
I think the problem is that those accounts aren't real system accounts, so might not be so easy to distinguish.
You could look at the WMI classes Win32_UserAccount and Win32_UserProfile and see if there are any properties in there that might indicate which user accounts are normal ones and which ones are the ones you mention. Specifically, maybe the 'SIDType' or 'AccountType' properties of Win32_UserAccount or maybe the Special property of the Win32_UserProfile class.
Might be other WMI classes that might be worth looking at as well.
Or there might be some way that you can query if a user account has the interactive logon right (which I assume those two accounts might not have normally).
Have you tried enumerating the Properties collection on DirectoryEntry?
using (DirectoryEntry dirEntry = new DirectoryEntry(strchild))
{
foreach (string strPropertyName in dirEntry.Properties.PropertyNames)
{
Console.WriteLine(strPropertyName + " " + dirEntry.Properties[strPropertyName].Value.ToString());
}
}
Other than that, you may have to do an LDAP search on Active Directory to match the UserName you have found to an ActiveDirectory user.
Have a look at this article.
http://www.codeproject.com/KB/system/everythingInAD.aspx
Have fun.
The following code will get you the local users that actually have local accessible folders.
var localDrives = Environment.GetLogicalDrives();
var localUsers = new List<string>();
var query = new SelectQuery("Win32_UserAccount") { Condition = "SIDType = 1 AND AccountType = 512" };
var searcher = new ManagementObjectSearcher(query);
foreach (ManagementObject envVar in searcher.Get())
{
foreach (string drive in localDrives)
{
var dir = Path.Combine(String.Format("{0}Users", drive), envVar["name"].ToString());
if (Directory.Exists(dir))
{
localUsers.Add(envVar["name"].ToString());
}
}
}
I want to create a quick application for people to resolve the name of a user stored in Active Directory from a set of credentials. Some applications only provide the user id and it is too much to expect an end user to fire up the Active Directory Users and Groups MMC snap-in.
Input would be something like "MYCORP\a_user" and output would be "Dave Smith" if that is what is stored in AD.
I want this to be able to run in my test domain and also in a multi-forest environment.
Can someone provide a sample that does this? Does retrieval of other attributes from AD such as telephone number follow the same pattern?
Target platform: .NET 2.0 and above.
Here's the code I use, taken from my authentication class:
string[] strUserName = username.Split("\\".ToCharArray());
using (var entry = new DirectoryEntry("LDAP://" + ADServer, ADServiceDomain + "\\" + ADServiceAccount, ADServicePassword))
using (var ds = new DirectorySearcher(entry, "sAMAccountName=" + strUserName[1])) {
ds.SearchScope = SearchScope.Subtree;
SearchResult result = ds.FindOne();
string fullname = result.Properties["displayName"][0].ToString();
}
System.DirectoryServices sucks. As you can see, it takes a ridiculous amount of code to do even the most basic things. I'd like to see a user authentication method that didn't require using exceptions for flow control.
Working with Active Directory is a bit painfull in C#, sure 3.5 adds some new classes to help, but for pure productivity I like to use Powershell and Quest's free PowerShell Commands for Active Directory
in which case the code looks something like
get-qaduser userid | select PhoneNumber,DisplayName
if you need this to run as part of your C# program, you can do that too
public static IEnumerable<PSObject> Invoke(string script, params object[] input)
{
IList errors = null;
using (var run = new RunspaceInvoke())
{
var psResults = run.Invoke(script, input, out errors);
if (errors != null && errors.Count > 0)
Debug.WriteLine(errors.Count);
foreach (PSObject res in psResults)
yield return res;
}
}
PSObject psUser = POSHelp.Invoke(
#"add-pssnapin Quest.ActiveRoles.ADManagement
($userid) = $input | % { $_ }
get-qaduser $userid", "auserid").Single();
Debug.WriteLine(psUser.Properties["DisplayName"].Value);
add a ref to Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll
See DirectorySearcher, loading the property "DisplayName".