When using DirectoryEntry, it is possible to set the CN of the new user account, but how to do it using UserPrincipal? The property is readonly.
// From : http://msdn.microsoft.com/en-us/magazine/cc135979.aspx
DirectoryEntry container = new DirectoryEntry("LDAP://ou=TechWriters,dc=fabrikam,dc=com");
// create a user directory entry in the container
DirectoryEntry newUser = container.Children.Add("cn=user1Acct", "user");
// add the samAccountName mandatory attribute
newUser.Properties["sAMAccountName"].Value = "User1Acct";
// save to the directory
newUser.CommitChanges();
But using UserPrincipal:
// For the example
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, null, "ou=TechWriters,dc=fabrikam,dc=com")
{
using (UserPrincipal user = new UserPrincipal(ctx, "User1Acct", "pwd", true))
{
// I would like to do :
user.DistinguishedName = "user1Acct";
//
user.Save();
}
}
Not the answer you want, but to my knowledge its not doable that way... The CN is 'protected' om the userprinciple class, as too much elsewhere relies on that being stable information.
I don't know why one would mix things up, but you could try this:
using (var ctx = new PrincipalContext(ContextType.Domain, null, "ou=TechWriters,dc=fabrikam,dc=com"))
{
using (var user = new UserPrincipal(ctx, "User1Acct", "pwd", true))
{
user.Save();
}
using (var entry = new DirectoryEntry("LDAP://cn=User1Acct;ou=TechWriters,dc=fabrikam,dc=com",null,null,AuthenticationTypes.Secure))
{
entry.Rename("cn=user1Acct");
}
}
(Maybe getting the LDAP string from the userPrinciple instead of hardcoding)
I do not have the possibillity to test this though..
Related
I am finding user name from Active Directory by passing email id. It is working fine. But it takes 30-40 seconds to get the username. Is there any other better way to find the username from Active Directory by email address?
Please refer to my code:
using (PrincipalContext context = new PrincipalContext(ContextType.Domain, "domainname"))
{
UserPrincipal userPrincipal = new UserPrincipal(context);
PrincipalSearcher principalSearch = new PrincipalSearcher(userPrincipal);
foreach (UserPrincipal result in principalSearch.FindAll())
{
if (result != null && result.EmailAddress != null && result.EmailAddress.Equals(user.Email, StringComparison.OrdinalIgnoreCase))
{
user.FirstName = result.GivenName;
user.LastName = result.Surname;
}
}
}
You don't need to enumerate all users to to find one of them! Try this code:
using (PrincipalContext context = new PrincipalContext(ContextType.Domain, "domainname"))
{
UserPrincipal yourUser = UserPrincipal.FindByIdentity(context, EmailAddress);
if (yourUser != null)
{
user.FirstName = yourUser.GivenName;
user.LastName = yourUser.Surname;
}
}
If that shouldn't work, or if you need to search for several criteria at once, used the PrincipalSearcher with the QBE (query-by-example) approach - search the one user you need - don't cycle through all users!
// create your domain context
using (PrincipalContext context = new PrincipalContext(ContextType.Domain, "domainname"))
{
// define a "query-by-example" principal -
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.EmailAddress = yourEmailAddress;
// 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.....
}
}
using System.DirectoryServices.AccountManagement;
// Lock user
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
UserPrincipal yourUser = UserPrincipal.FindByIdentity(context, logonName);
if (yourUser != null)
{
if(!yourUser.IsAccountLockedOut())
{
yourUser.Enabled = False;
yourUser.Save();
}
}
}
I am logged in under user User001 under domain DomainA and with password Pass001.
I use this code
//var principalContext = new PrincipalContext(
//ContextType.Domain,
//"DomainA",
//"User001",
//"Pass001");
var principalContext = new PrincipalContext(
ContextType.Domain,
domain,
userName,
password);
var userPrincipal = new UserPrincipal(principalContext);
And userPrincipal is always NULL.
How to fix it?
Somehow this code I found is working fine...
using (var context = new PrincipalContext(ContextType.Domain))
{
using (UserPrincipal principal = UserPrincipal.FindByIdentity(context, userName))
{
var uGroups = principal.GetGroups();
foreach (var group in uGroups)
{
Debug.WriteLine(group.DisplayName);
}
}
}
Creating a web service which talks to Active Directory to verify users and determine which groups they belong to.
I started out with the verification process, and got this working:
public bool AuthenticateAdUser(string username, string password)
{
//in the real code, these come from config
string domain = "TestDomain";
string server = 666.666.666.666;
string authType = "Basic";
string useSsl = "false";
AuthType atype = (AuthType)Enum.Parse(typeof(AuthType), authType);
using (var ldapConnection = new LdapConnection(server))
{
var networkCredential = new NetworkCredential(username, password, domain);
ldapConnection.SessionOptions.SecureSocketLayer = Convert.ToBoolean(useSsl);
ldapConnection.AutoBind = false;
ldapConnection.AuthType = atype;
ldapConnection.Bind(networkCredential);
}
// If the bind succeeds, the credentials are valid
return true;
}
However, I'm not clear on how I can use that LdapConnection object to work with groups. The documentation and examples suggest you use PrinicpalContext for that purpose. So I tried this.
string domain = "TestDomain";
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain))
{
using (PrincipalSearchResult<Principal> src = UserPrincipal.FindByIdentity(pc, username).GetGroups(pc))
{
src.ToList().ForEach(sr => result.Add(sr.SamAccountName));
}
}
This fails, claiming it can't contact the Active Directory server. Using a DNS style name ("TestDomain.local") doesn't seem to help.
This does at least spin up a network principal:
string server = "666.666.666.666";
using (PrincipalContext pc = new PrincipalContext(ContextType.Machine, server))
{
using (PrincipalSearchResult<Principal> src = UserPrincipal.FindByIdentity(pc, username).GetGroups(pc))
{
src.ToList().ForEach(sr => result.Add(sr.SamAccountName));
}
}
But when you try and do anything with it, it fails with "Network path not found".
Any ideas on why the Principal won't work, or how I can use the LdapConnection to query groups?
So i'm looking to grab some ldap values, and insert them into a database with encryption. I've got the insert working but i need to check if the user is still part of the group and if not remove them from the DB, and if there was a new user added it inserts them instead of inserting existing users. Can you give me some direction on best practices for this? I'd prefer not to truncate the table and re-insert all.
try
{
/* Connection to Active Directory */
DirectoryEntry deBase = new DirectoryEntry("LDAP://" + txtLDAP.Text + ":" + txtLDapPort.Text + "/" + txtBadeDN.Text, txtUsername.Text, txtPassword.Text, AuthenticationTypes.Secure);
/* Directory Search*/
DirectorySearcher dsLookForGrp = new DirectorySearcher(deBase);
dsLookForGrp.Filter = String.Format("(cn={0})", txtGroup.Text);
dsLookForGrp.SearchScope = SearchScope.Subtree;
dsLookForGrp.PropertiesToLoad.Add("distinguishedName");
SearchResult srcGrp = dsLookForGrp.FindOne();
/* Directory Search
*/
DirectorySearcher dsLookForUsers = new DirectorySearcher(deBase);
dsLookForUsers.Filter = String.Format("(&(objectCategory=person)(memberOf={0}))", srcGrp.Properties["distinguishedName"][0]);
dsLookForUsers.SearchScope = SearchScope.Subtree;
dsLookForUsers.PropertiesToLoad.Add("objectSid");
dsLookForUsers.PropertiesToLoad.Add("sAMAccountName");
SearchResultCollection srcLstUsers = dsLookForUsers.FindAll();
StringBuilder sbUsers = new StringBuilder();
foreach (SearchResult sruser in srcLstUsers)
{
SecurityIdentifier sid = new SecurityIdentifier((byte[])sruser.Properties["objectSid"][0], 0);
string ConnString = "ConnectionString Removed";
string SqlString = "spInsertADAuthorization";
using (OleDbConnection conn = new OleDbConnection(ConnString))
{
using (OleDbCommand cmd = new OleDbCommand(SqlString, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("AD_Account", SpartaCrypto.SpartaEncryptAES(sruser.Properties["sAMAccountName"][0].ToString(), "thisisasharedsecret"));
cmd.Parameters.AddWithValue("AD_SID", SpartaCrypto.SpartaEncryptAES(sid.ToString(), "thisisasharedsecret"));
cmd.Parameters.AddWithValue("AD_EmailAddress", "user#host.com");
cmd.Parameters.AddWithValue("DateImported", DateTime.Now.ToString());
cmd.Parameters.AddWithValue("Active", 1);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
}
lblResults.Text = srcLstUsers.Count + " Users granted access.";
}
}
catch (Exception ex)
{
if (ex.Message.Contains("Logon failure"))
{
lblResults.Text = "Logon Failure. Check your username or password.";
}
if (ex.Message.Contains("The server is not operational"))
{
lblResults.Text = "LDAP Error. Check your hostname or port.";
}
if (ex.Message.Contains("Object reference not set to an instance of an object"))
{
lblResults.Text = "LDAP Error. Check your hostname, port, or group name and try again.";
}
}
Since 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
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.GivenName = "Bruce";
// 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.....
}
For working with a single principal, the programming interface is also much nicer:
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
if(user != null)
{
// do something here... you can access most of the commonly used properties easily
user.GivenName = "....";
user.Surname = "......";
user.SamAccountName = ".....";
}
// 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 much easier to play around with users and groups in AD!
I am working with LDAP and I am new to this.
Is there a way to get the domain when you only know the username, password, servername
I am trying to do this:
string ldapPath = "LDAP://serverName";
string uid = username;
string password = pwd;
string qry = String.Format("(uid={0})", uid);
string adsPath = String.Empty;
try
{
DirectoryEntry nRoot = new DirectoryEntry(ldapPath, null, null, AuthenticationTypes.Anonymous);
DirectorySearcher ds = new DirectorySearcher(nRoot, qry);
SearchResult sr = ds.FindOne();
if (sr != null)
{
// we want to retrieve the DN like this: "uid=myuser,ou=People,dc=findlay,dc=edu
ldapPath = sr.Path; //update where we will bind next
}
This does not work unless I change
string ldapPath = "LDAP://serverName";
to
string ldapPath = "LDAP://serverName/DC=mydomain,DC=com";
Any help..??
Thanks
Edit rootDSE
string defaultNamingContext;
using (DirectoryEntry rootDSE = new DirectoryEntry("LDAP://serverName/rootDSE", null, null, AuthenticationTypes.Anonymous))
{
defaultNamingContext = rootDSE.Properties["rootDomainNamingContext"].Value.ToString();
}
I too feel this is the solution but it is currently not working for me.. please help!
RootDSE is not server-bound - try this:
string defaultNamingContext;
using (DirectoryEntry rootDSE = new DirectoryEntry("LDAP://rootDSE", null, null, AuthenticationTypes.Anonymous))
{
defaultNamingContext = rootDSE.Properties["defaultNamingContext"].Value.ToString();
}
Or if you're on .NET 3.5 and newer, you could use PrincipalContext instead, which can be constructed without any path - it will just pick up the default domain you're connected to:
PrincipalContext context = new PrincipalContext(ContextType.Domain);
You should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here (that's .NET 3.5 and newer):
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
If :
using (DirectoryEntry rootDSE = new DirectoryEntry("LDAP://serverName/DC=mydomain,DC=com")
{
...
}
works, have you try (without being anonymous):
string defaultNamingContext;
using (DirectoryEntry rootDSE = new DirectoryEntry("LDAP://serverName/rootDSE")
{
defaultNamingContext = rootDSE.Properties["rootDomainNamingContext"].Value.ToString();
}
or
using (DirectoryEntry rootDSE = new DirectoryEntry("LDAP://serverName/rootDSE", user, password)
{
defaultNamingContext = rootDSE.Properties["rootDomainNamingContext"].Value.ToString();
}
It works for me, from a computer not in the domain.
you can try like this
// Method call
string netBiosName = GetNetBiosName(LDAP://CN=Partitions,CN=Configuration,DC=<DomainName>,DC=<local|com>, "<userName"", "<password>");
// Method call
// Method Definition
private string GetNetBiosName(string ldapUrl, string userName, string password)
{
string netbiosName = string.Empty;
DirectoryEntry dirEntry = new DirectoryEntry(ldapUrl,userName, password);
DirectorySearcher searcher = new DirectorySearcher(dirEntry);
searcher.Filter = "netbiosname=*";
searcher.PropertiesToLoad.Add("cn");
SearchResultCollection results = searcher.FindAll();
if (results.Count > 0)
{
ResultPropertyValueCollection rpvc = results[0].Properties["CN"];
netbiosName = rpvc[0].ToString();
}
return netbiosName;
}
pls take a look at this link for more info
You should be able to get the domain by just calling RootDse.