AD request with C# - c#

I'm trying to get all users from AD and their passwords expiration dates with the following code, but got the error.
System.InvalidOperationException: 'The Principal object must be persisted before this method can be called.'
The code:
PrincipalContext domain = new PrincipalContext(ContextType.Domain,"AAA","OU=USERS,OU=TEST,DC=AAA,DC=AA, DC=A");
UserPrincipal user = new UserPrincipal(domain);
PrincipalSearcher pS = new PrincipalSearcher(user);
foreach (UserPrincipal result in pS.FindAll())
{
if (result != null && result.DisplayName != null)
{
DirectoryEntry entry = (DirectoryEntry)user.GetUnderlyingObject();
IADsUser native = (IADsUser)entry.NativeObject;
Console.WriteLine(result.DisplayName, native.PasswordExpirationDate);
}
}

The exception means that you're calling GetUnderlyingObject() on a UserPrincipal object that hasn't been saved.
You're calling GetUnderlyingObject() on the user object that you created as the filter for your PrincipalSearcher. I think you intended to call it on result:
DirectoryEntry entry = (DirectoryEntry)result.GetUnderlyingObject();

Related

How can I find out if a dynamic property exists in C#

I am iterating through some Active Directory PrincipalSearcher Principal results using the code below. In my foreach loop where I am assigning result properties to my _user object I can break in debug and see a result.EmailAddress value. When I try to assign result.EmailAddress to _user.EmailAdress the code will not even compile. I'm guessing EmailAddress is a dynamic property of result. Is there a way to check for this property so I can add the users AD email address to my _user object?
private static void GetAllActiveDirectoryUsers()
{
PrincipalContext context = new PrincipalContext(
ContextType.Domain, Environment.UserDomainName);
UserPrincipal user = new UserPrincipal(context);
// create a principal searcher for running a search operation
PrincipalSearcher pS = new PrincipalSearcher(user);
// run the query
PrincipalSearchResult<Principal> results = pS.FindAll();
foreach (Principal result in results)
{
Console.WriteLine(result.DisplayName);
Console.ReadKey();
User _user = new User();
_user.Description = result.Description;
_user.DisplayName = result.DisplayName;
_user.DistinguishedName = result.DistinguishedName;
_user.Guid = result.Guid ?? null;
_user.Name = result.Name;
_user.Sid = result.Sid?.ToString();
Users.Add(_user);
}
}
It's not a dynamic property. EmailAddress is a property of the UserPrincipal class, which inherits from Principal. Your result is actually of type UserPrincipal, which is why you see the property when you debug, but you are accessing it as type Principal, which doesn't have a property called EmailAddress, so it is not available to you in your code.
If you want access to the EmailAddress property, you need to cast your result to UserPrincipal. Since you are sure all of your results will be user objects, then you can do that in the foreach:
foreach (UserPrincipal result in results)
You will want to get the DirectoryEntry object from the Principal and query it's properties.
Something like this - assuming email is stored in the 'mail' attribute.
var directoryEntry = result.GetUnderlyingObject() as DirectoryEntry;
if (directoryEntry != null && directoryEntry.Properties.Contains("mail"))
{
_user.EmailAddress = directoryEntry.Properties[property].Value.ToString();
}
Here is an extension method that uses the above. It simply takes a string for the attribute that you are searching for.
public static string GetPropertyValue(this Principal principal, string property)
{
var directoryEntry = principal.GetUnderlyingObject() as DirectoryEntry;
if (directoryEntry != null && directoryEntry.Properties.Contains(property))
{
return directoryEntry.Properties[property].Value.ToString();
}
return null;
}

Active Directory users import in MVC

I am trying to fetch all the users of particular group from Active Directory of LDAP server. Authentication becomes success but i am getting null in result.
Following is my code.
Domain-172.11.12.123
Email-sample#email.com
password-123456
using (var context = new DirectoryEntry(user.Domain, user.Email, user.Password, AuthenticationTypes.Secure))
{
try
{
string FirstName;
string LastName;
string ADUserName;
string Email;
using (var searcher = new DirectorySearcher(context))
{
searcher.Filter = "(&((&(objectCategory=Person)(objectClass=User)))(samaccountname='user3'))";
List<string> Adusers = new List<string>();
System.DirectoryServices.SearchResult result = searcher.FindOne();
}
}
catch (Exception ex)
{
TempData["message"] = "error";
return RedirectToAction("Index", "ADuserList");
}
}
What wrong is going on.
Thanks in advance
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace.
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context for the currently connected AD domain
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// find the group in question
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, "YourGroupNameHere");
// if found....
if (group != null)
{
// iterate over members
foreach (Principal p in group.GetMembers())
{
Console.WriteLine("{0}: {1}", p.StructuralObjectClass, p.DisplayName);
// do whatever you need to do to those members
}
}
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!
Read more about it here:
MSDN docs on System.DirectoryServices.AccountManagement
Update: in order to get all the users of a given OU, the approach is quite different.
You need to create a separate PrincipalContext that defines what OU you're interested in - then you need to use a PrincipalSearcher to get all the users from that OU:
// create your domain context and define what OU to use:
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, null, "OU=YourOU,OU=SubOU,dc=YourCompany,dc=com"))
{
// define a "query-by-example" principal - here, we search for any UserPrincipal
UserPrincipal qbeUser = new UserPrincipal(ctx);
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// find all matches
foreach(var found in srch.FindAll())
{
// do whatever here - "found" is of type "Principal" - it could be user, group, computer.....
}
}

UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(ctx, found.DisplayName) returns null

Not sure why this happens but when I run this code it works on one server but not on another.
Both servers return a correct found.DisplayName however only one server returns a value for oUserPrincipal the other returns a null value.
Line of error:
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(ctx, found.DisplayName) returns null
dynamic config = _getExpandoFromXml("config.xml");
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, config.activeDirectory.sDomain, config.activeDirectory.sDefaultOU,config.mailServer.user, config.mailServer.pass);
UserPrincipal user = new UserPrincipal(ctx);
PrincipalSearcher search = new PrincipalSearcher(user);
Console.WriteLine("before foreach");
foreach (Principal found in search.FindAll())
{
try{
if (found.DisplayName == null)
{
Console.WriteLine("found.Dispalyname is null");
}
else
{
Console.Write("Dispalyname: ");
Console.WriteLine(found.DisplayName);
}
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(ctx, found.DisplayName);
Console.Write("looking for user: ");
Console.WriteLine(found.DisplayName);
Console.WriteLine("after findbyidentiy");
if (oUserPrincipal == null)
{
Console.WriteLine("oUserPrinciapal is null");
}
if (oUserPrincipal.LastPasswordSet == null)
{
Console.WriteLine("lastpasswordset is null");
}
DateTime? dateOrNull = oUserPrincipal.LastPasswordSet;
Console.WriteLine("after LastPasswordSet");
FindByIdentity can only search on a handful of properties. These are "any format that is contained in the IdentityType enumeration".
Name is a valid option but DisplayName is not listed so you will probably get results where the DisplayName and Name happen to be the same and it will fail otherwise.
Using:
var oUserPrincipal = UserPrincipal.FindByIdentity(ctx, found.Name);
or
var oUserPrincipal = UserPrincipal.FindByIdentity(ctx, found.SamAccountName);
should work.
There is also a three parameter version of FindByIdentity that allows you to specify the property you would like to search on.
In my case, I found the issue was that it wasn't connecting to the AD server. If I hovered over oPrincipalContext, its property of ConnectedServer showed that it threw an exception of type System.DirectoryServices.DirectoryServicesCOMException. If this happens, a restart of services on the Domain Controller should work. We found it can happen during high-login times, since we only have 1 DC on our dev network.

How to get username and SID for user by a domain name in ldap

I am trying to get the user information for a specific domain which will be the input of the program. On the basis of the domain name it should return the list of the users name/ or NT Id and SID of the user. I am new for the ldap programming can any one help me for get this list.
If you're on .NET 3.5 and up and talking about Active Directory, then you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
if(user != null)
{
// do something here....
var usersSid = user.Sid;
// not sure what you mean by "username" - the "DisplayName" ? The "SAMAccountName"??
var username = user.DisplayName;
var userSamAccountName = user.SamAccountName;
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!
Update: if you need to loop through all the users of a domain - try this:
You can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// create your domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// define a "query-by-example" principal - here, we search for a UserPrincipal
UserPrincipal qbeUser = new UserPrincipal(ctx);
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// find all matches
foreach(var found in srch.FindAll())
{
UserPrincipal user = found as UserPrincipal;
if(user != null)
{
// do whatever here
var usersSid = user.Sid;
// not sure what you mean by "username" - the "DisplayName" ?
var username = user.DisplayName;
var userSamAccountName = user.SamAccountName;
}
}
Update #2: if you can't (or don't want to) use the S.DS.AM approach - which is the easiest, for Active Directory, by far - then you need to fall back to the System.DirectoryServices classes and methods:
// define the root of your search
DirectoryEntry root = new DirectoryEntry("LDAP://dc=YourCompany,dc=com");
// set up DirectorySearcher
DirectorySearcher srch = new DirectorySearcher(root);
srch.Filter = "(objectCategory=Person)";
srch.SearchScope = SearchScope.Subtree;
// define properties to load
srch.PropertiesToLoad.Add("objectSid");
srch.PropertiesToLoad.Add("displayName");
// search the directory
foreach(SearchResult result in srch.FindAll())
{
// grab the data - if present
if(result.Properties["objectSid"] != null && result.Properties["objectSid"].Count > 1)
{
var sid = result.Properties["objectSid"][0];
}
if(result.Properties["displayName"] != null && result.Properties["displayName"].Count > 0)
{
var userName = result.Properties["displayName"][0].ToString();
}
}

Active Directory Search getting Object reference not set

The below code works on my local machine by returning the user's full name from Active Directory:
string principal = System.Web.HttpContext.Current.Request.LogonUserIdentity.Name.Remove(0, 12);
string filter = string.Format("(&(ObjectClass={0})(sAMAccountName={1}))", "person", principal);
string[] properties = new string[] { "fullname" };
DirectoryEntry adRoot = new DirectoryEntry("LDAP://myserver.com");
adRoot.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher searcher = new DirectorySearcher(adRoot);
searcher.SearchScope = SearchScope.Subtree;
searcher.ReferralChasing = ReferralChasingOption.All;
searcher.PropertiesToLoad.AddRange(properties);
searcher.Filter = filter;
SearchResult result = searcher.FindOne();
DirectoryEntry directoryEntry = result.GetDirectoryEntry();
string displayName = directoryEntry.Properties["displayName"][0].ToString();
if (string.IsNullOrEmpty(displayName) == false)
{
return displayName;
}
When I publish it to the development server I get the following error:
System.NullReferenceException: Object reference not set to an instance
of an object.
The error is thrown on the following line:
DirectoryEntry directoryEntry = result.GetDirectoryEntry();
I have tried
DirectoryEntry adRoot = new DirectoryEntry("LDAP://" + domain, AdAdminUsername, AdAdminPassword, AuthenticationTypes.Secure);
but still no joy.
Any ideas?
Thanks!
Just from outside : your string called principal is built by the following :
string principal = System.Web.HttpContext.Current.Request.LogonUserIdentity.Name.Remove(0, 12);
Have you log 'System.Web.HttpContext.Current.Request.LogonUserIdentity.Name' on your dev server ? This static construction may be the begining of you troubles because if principal is'nt what you think it is, the result of FindOne may be NULL.
In your current code, you must check for NULL after the call to FindOne() - it could return a NULL value if no matching directory entry was found.
See the MSDN docs:
If more than one entry is found during the search, only the first
entry is returned. If no entries are found to match the search
criteria, a null reference (Nothing in Visual Basic) is returned.
Plus, you should also always check for the presence of a property before accessing it - it could be absent.....
Therefore, your code should be something like this:
SearchResult result = searcher.FindOne();
if(result != null)
{
DirectoryEntry directoryEntry = result.GetDirectoryEntry();
if(directoryEntry.Properties["displayName"] != null &&
directoryEntry.Properties["displayName"].Length > 0)
{
string displayName = directoryEntry.Properties["displayName"][0].ToString();
if (!string.IsNullOrEmpty(displayName))
{
return displayName;
}
}
}
But: if you're on .NET 3.5 or newer, you should check out the System.DirectoryServices.AccountManagement namespace which makes a lot of things a lot easier when dealing with Active Directory.
You can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// create your domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// define a "query-by-example" principal - here, we search for a UserPrincipal
// and with the first name (GivenName) of "Bruce"
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.SamAccountName = "whatever you're looking for.....";
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// find all matches
foreach(var found in srch.FindAll())
{
// do whatever here - "found" is of type "Principal" - it could be user, group, computer.....
}
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement
Of course, depending on your need, you might want to specify other properties on that "query-by-example" user principal you create:
Surname (or last name)
DisplayName (typically: first name + space + last name)
SAM Account Name - your Windows/AD account name
User Principal Name - your "username#yourcompany.com" style name
You can specify any of the properties on the UserPrincipal and use those as "query-by-example" for your PrincipalSearcher.

Categories