ASP.NET Active Directory Search - asp.net-mvc-4

I'm trying to create an intranet Website on ASP.NET MVC 4 using Windows Login. I have successfully done the windows login. The only thing I am stuck up with is searching the active directory with partial username. I tried searching the web and stackoverflow website but still couldn't find the answer.
DirectoryEntry directory = new DirectoryEntry("LDAP://DC=NUAXIS");
string filter = "(&(cn=jinal*))";
string[] strCats = { "cn" };
List<string> items = new List<string>();
DirectorySearcher dirComp = new DirectorySearcher(directory, filter, strCats, SearchScope.Subtree);
SearchResultCollection results = dirComp.FindAll();

You can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// create your domain context
using (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 "Jinal*"
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.GivenName = "Jinal*";
// create your principal searcher passing in the QBE principal
using (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. Or see the MSDN documentation on the System.DirectoryServices.AccountManagement namespace.
Of course, depending on your need, you might want to specify other properties on that "query-by-example" user principal you create:
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.

Your current code is on the right track.
I think you had your wildcard backwards.
Consider this:
search.Filter = string.Format("(&(sn={0}*)(givenName={1}*)(objectSid=*))", lastName, firstName);

Related

Get AD Guid from HttpContext.Current.User

I have tried many, many different ways, to get this data. But I can't get it to work.
I have a MVC4 application, hooked up with Active Directory. But I need the users AD GUID.
I tried:
(Guid)Membership.GetUser(User.Identity.Name).ProviderUserKey;
WebSecurity.CurrentUserId;
But none of them work.
If you're on .NET 3.5 and up, 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
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, User.Identity.Name);
if(user != null)
{
Guid userGuid = user.Guid ?? Guid.Empty;
}
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!
I managed to solve it (Not pretty...):
string login = HttpContext.Current.User.Identity.Name;
string domain = login.Substring(0, login.IndexOf('\\'));
string userName = login.Substring(login.IndexOf('\\') + 1);
DirectoryEntry domainEntry = new DirectoryEntry("LDAP://" + domain);
DirectorySearcher searcher = new DirectorySearcher(domainEntry);
searcher.Filter = string.Format("(&(objectCategory=person)(objectClass=user)(sAMAccountName={0}))",userName);
SearchResult searchResult = searcher.FindOne();
DirectoryEntry entry = searchResult.GetDirectoryEntry();
Guid objectGuid = entry.Guid;
The original code used : entry.NativeGuid, but I changed because of Little / Big endian "problems"
entry.Guid has the same "format" as in AD.

Adding users to AD using LDAP

I'm writing an application that will add users to Active Directory. I'm trying to use this code to connect to the "Users" shared folder in AD
LDAP://celtestdomdc1.celtestdom.local/CN=Users,DC=celtestdom,DC=local
However it adds the user in with the shared folders, instead of within the "Users" shared folder. Shouldn't CN=Users mean it will add it to the "Users" folder?
Thanks
If you're creating a user, you need to
bind to the container you want to create the user in
create the new user account as a child of that container
Just by setting the LDAP path, you are not defining where the user will go!
Try something like this (C# sample - should be trivial to convert to VB.NET):
DirectoryEntry cnUsers = new DirectoryEntry("LDAP://CN=Users,DC=celtestdom,DC=local");
// create a user directory entry in the container
DirectoryEntry newUser = container.Children.Add("cn=NewUserAccount", "user");
// add the samAccountName mandatory attribute
newUser.Properties["sAMAccountName"].Value = "NewUser";
// add any optional attributes
newUser.Properties["givenName"].Value = "User";
newUser.Properties["sn"].Value = "One";
// save to the directory
newUser.CommitChanges();
// set a password for the user account
// using Invoke method and IadsUser.SetPassword
newUser.Invoke("SetPassword", new object[] { "pAssw0rdO1" });
// require that the password must be changed on next logon
newUser.Properties["pwdLastSet"].Value = 0;
// save to the directory
newUser.CommitChanges();
Or if you're using .NET 3.5 or newer, you could also use the new System.DirectoryServices.AccountManagement namespace that makes lots of things easier.
Then the code looks a bit simpler:
// create a context for a domain and define "base" container to use
PrincipalContext ctx = new PrincipalContext(ContextType.Domain,
"celtestdom", "CN=Users,DC=celtestdom,DC=local");
// create a user principal object
UserPrincipal user = new UserPrincipal(ctx, "NewUser", "pass#1w0rd01", true);
// assign some properties to the user principal
user.GivenName = "User";
user.Surname = "One";
// force the user to change password at next logon
user.ExpirePasswordNow();
// save the user to the directory
user.Save();
Check out more about the System.DirectoryServices.AccountManagement (S.DS.AM) namespace here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement

How to manipulate LDAP using JNDI on websphere?

I am facing a problem with an LDAP operation. I want to dynamically add a member to an LDAP group when selected by the user from GUI / browser. I paste the code below which works perfectly well when I run it in a Test class (using com.sun.jndi.ldap.LdapCtxFactory). But, when I package it in my build, deploy on websphere app server 7.0 (using com.ibm.websphere.naming.WsnInitialContextFactory), and invoke this method according to user's selection, then I get the error below. I wonder what's wrong I am doing. Doesn't WAS provide implementation of ldap connection factory? I also tried deploying on WAS with the sun's ldap which otherwise works on the Test class, but I am getting the same exception as below. I'd appreciate if anybody can give a clue.
Problem adding member: javax.naming.OperationNotSupportedException: [LDAP: error code 53 - 00000561: SvcErr: DSID-031A120C, problem 5003 (WILL_NOT_PERFORM), data 0
My Code:
public class LDAPManager
{
String GROUPS_OU = "cn=users,dc=mit,dc=hq,dc=com";
public Boolean addMember(String user, String group)
{
Hashtable env = new Hashtable();
String adminName = "CN=Administrator,CN=Users,DC=mit,DC=hq,DC=com";
String adminPassword = "asdfasdf21Q";
String ldapURL = "ldap://mybox451Dev.mit.hq.com:389";
String userName = "CN="+user+",CN=Users,DC=mit,DC=hq,DC=com";
String groupName = "CN="+group+",CN=Users,DC=mit,DC=hq,DC=com";
//env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.ibm.websphere.naming.WsnInitialContextFactory");
//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, "ldap://mybox451Dev.mit.hq.com:389");
try {
// Create the initial directory context
InitialDirContext ctx = new InitialDirContext(env);
//Create a LDAP add attribute for the member attribute
ModificationItem mods[] = new ModificationItem[1];
mods[0]= new ModificationItem(DirContext.ADD_ATTRIBUTE, new BasicAttribute("member", userName));
//update the group
ctx.modifyAttributes(groupName,mods);
ctx.close();
//System.out.println("Added " + userName + " to " + groupName);
}
catch (NamingException e) {
System.err.println("Problem adding member: " + e);
}
return true;
}
}
I got it solved. Posting solution here, hope this helps someone.
Use the standard JNDI context of sun, not websphere.
Additional properties I was missing in the hashtable, once I added them, it worked like a charm.
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
//env.put(Context.INITIAL_CONTEXT_FACTORY,"com.ibm.websphere.naming.WsnInitialContextFactory");
//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);
env.put(Context.URL_PKG_PREFIXES, "com.sun.jndi.url");
env.put(Context.REFERRAL, "ignore");
Well, it's been more than a year since this question has been asked; so, I don't know answering will add any value. But, here it is. See WAS Javadocs for details on how what that factory class actually does and how it works. You may need to adjust your jndiprovider.properties file for WAS.

Searching user by id by exact match in LDAP (SharePoint 2010 people picker)

I am trying to search a user in the LDAP and resolve his name in SharePoint PeoplePicker
User types user's idsid in the PeoplePicker and then hit CheckName
The code calls SearchSingleUser() with the typed userid.
Example: I type 'xyz' and hit CheckName
The method below would then search LDAP for users with SamAccountName='xyz' for exact match. If match found then it should resolve the idsid in peoplepicker
If the LDAP has Domain\xyz but user types xyz, it won't match and won't resolve
But what I am seeing is that the name gets half resolved.
Any clue what I am missing as far as searching for exact match of a property?
This is my code:
public static string _LDAPSearchDefSingleUser = "(&(objectClass=user)(SamAccountName={0}))";
public static SearchResultCollection SearchSingleUser(string searchPattern)
{
using (DirectoryEntry root = new DirectoryEntry(ldapPath, username, password))
{
root.AuthenticationType = AuthenticationTypes.None;
string filter = string.Format(_LDAPSearchDefSingleUser, searchPattern);
using (DirectorySearcher searcher = new DirectorySearcher(root))
{
searcher.ReferralChasing = ReferralChasingOption.All;
searcher.SearchScope = SearchScope.Subtree;
searcher.Filter = filter;
searcher.PropertiesToLoad.Add("objectclass");
searcher.PropertiesToLoad.Add("SamAccountName");
SearchResultCollection results = searcher.FindAll();
return results;
}
}
}
Not sure to understantd your question, but I confirm that the following filter :
(&(objectClass=user)(SamAccountName=xyz))
in an LDAP search returns only THE object of class user with the attribute SamAccountName exactly equal to 'xyz'.
In your case, if you've got multiple match, it's because you enter '*xyz' or '*xyz*'.
For your information I use quite the same code and it works so.

Simple login for multi-domain intranet?

I have an intranet server on a Windows domain (server is Windows 2003, IIS6, NTFS permissions). It is on the domain Domain01. I have users from two domains in the same forest that access this intranet: Domain01 and Domain02 (DCs also running Windows 2003). Currently, the users are required to login by entering either:
Domain01\username or username#Domain01
My users are completely and thoroughly confused by having to enter the domain each time they log in.
Is there any way to simply allow them to log in by entering just their username and password WITHOUT the domain? For example, have the server try Domain01 by default, and if the login fails to try Domain02?
NOTE: I would like to do this via IIS or server settings if possible, rather than programmatically (for reference, I am using ASP.NET 2.0).
Yes. Usually what I do is do a global catalog search using the supplied user name as the sAMAccountName. Doing this with a PrincipalSearcher requires getting the underlying DirectorySearcher and replacing it's SearchRoot. Once I find the corresponding user object I extract the domain from the user object's path and use that as the domain for the authentication step. How you do the authentication varies depending on what you need it to do. If you don't need impersonation you can use PrincipalContext.ValidateCredentials to make sure that the username/password match using a PrincipalContext that matches the domain of the user account that you previously found. If you need impersonation check out this reference.
// NOTE: implement IDisposable and dispose of this if not null when done.
private DirectoryEntry userSearchRoot = null;
private UserPrincipal FindUserInGlobalContext( string userName )
{
using (PrincipalSearcher userSearcher = new PrincipalSearcher())
{
using (PrincipalContext context
= new PrincipalContext( ContextType.Domain ))
{
userSearcher.QueryFilter = new UserPrincipal( context );
DirectorySearcher searcher
= (DirectorySearcher)userSearcher.GetUnderlyingSearcher();
// I usually set the GC path from the existing search root
// by doing some string manipulation based on our domain
// Your code would be different.
string GCPath = ...set GC path..
// lazy loading of the search root entry.
if (userSearchRoot == null)
{
userSearchRoot = new DirectoryEntry( GCPath );
}
searcher.SearchRoot = userSearchRoot;
using (PrincipalContext gcContext =
new PrincipalContext( ContextType.Domain,
null,
GCPath.Replace("GC://",""))
{
UserPrincipal userFilter = new UserPrincipal( gcContext );
userFilter.SamAccountName = userName;
userSearcher.QueryFilter = userFilter;
return userSearcher.FindOne() as UserPrincipal;
}
}
}
}