How can I get DOMAIN\USER from an AD DirectoryEntry? - c#

How can I get the Windows user and domain from an Active Directory DirectoryEntry (SchemaClassName="user") object?
The user name is in the sAMAccountName property but where can I look up the domain name?
(I can't assume a fixed domain name because the users are from various subdomains.)

This assumes that results is a SearchResultCollection obtained from a DirectorySearcher, but you should be able to get the objectsid from a DirectoryEntry directly.
SearchResult result = results[0];
var propertyValues = result.Properties["objectsid"];
var objectsid = (byte[])propertyValues[0];
var sid = new SecurityIdentifier(objectsid, 0);
var account = sid.Translate(typeof(NTAccount));
account.ToString(); // This give the DOMAIN\User format for the account

You won't find what you're looking for in the DirectoryEntry, unfortunately.
You have the sAMAccountName which typically is something like myuser (without the domain). You have the distinguishedName which is something like LDAP://cn=joe myuser,cn=Users,dc=yourCompany,dc=com. You also have a userPrincipalName but that's usually a name in the format of joeUser#mycompany.com.
But you won't find any attribute that has the domain\MyUser in it, unfortunately. You'll have to put that together from your information about the domain name, and the sAMAccountName of the DirectoryEntry.
For more information and some excellent Excel sheets on all the LDAP and WinNT properties in System.DirectoryServices, check out the Hilltop Lab website by ADSI MVP Richard Mueller.
Marc

To get the DirectoryEntry domain name you can use recursion on
directoryEntry.Parent.
And then if directoryEntry.SchemaClassName == "domainDNS"
you can get the domain name like this:
directoryEntry.Properties["Name"].Value

I found a partitions container in CN=Partitions,CN=Configuration that contains all domains.
When you match the user to the partion you can read the real domain name from the nETBIOSName+"\"+sAMAccountName property.

public static string GetDomainNameUserNameFromUPN(string strUPN)
{
try
{
WindowsIdentity wi = new WindowsIdentity(strUPN);
WindowsPrincipal wp = new WindowsPrincipal(wi);
return wp.Identity.Name;
}
catch (Exception ex)
{
}
return "";
}

If you are using the System.DirectoryServices libraries, you should have a SearchResultsCollection from a DirectorySearcher.
Within each SearchResult's Properties collection, there is a "distinguishedname" property. That will contain all the DC parts that make up the domain your directory entry belongs to.

I'm extending a previous answer by #laktak to provide the details of what he meant.
There is a partitions container in CN=Partitions,CN=Configuration that contains all domains which gives you the cn which is the Netbios domain name and the nCName property that contains the distinguishedName prefix a user will have if they are in this domain.
So start by searching ldap for (objectClass=*) in CN=Partitions,CN=Configuration and store the (cn, nCName) pairs of each result to a map.
Next you query ldap using (sAMAccountName=USERIDHERE) and get the distinguishedName from the user. Now go through the (cn, nCName) pairs and find the nCName that prefixes the distinguishedName from the user, and the corresponding cn is your desired Domain name.

I wrote this pieces of code for my own usage (in VB.net, easy translation) :
<System.Runtime.CompilerServices.Extension()>
Public Function GetDomainFQDN(ByVal Entry As DirectoryServices.DirectoryEntry) As String
Try
While Entry.SchemaClassName <> "domainDNS"
Entry = Entry.Parent
End While
Dim DN As String = Entry.Properties("DistinguishedName").Value
Return DN.Replace("DC=", "").Replace(",", ".")
Catch ex As Exception
Debug.WriteLine(ex.ToString)
Return String.Empty
End Try
End Function
<System.Runtime.CompilerServices.Extension()>
Public Function GetDomainNetbiosName(ByVal Entry As DirectoryServices.DirectoryEntry) As String
Try
While Entry.SchemaClassName <> "domainDNS"
Entry = Entry.Parent
End While
Return Entry.Properties("Name").Value
Catch ex As Exception
Debug.WriteLine(ex.ToString)
Return String.Empty
End Try
End Function

I feel obligated to add my answer which was inspired from Nicholas DiPiazza's answer here. Hope this PowerShell code helps someone!
$hash = #{} //this contains the map of CN and nCNAME
$Filter = '(nETBIOSName=*)'
$RootOU = "CN=Partitions,CN=Configuration,DC=DOMAIN,DC=LOCAL" //Change this to your org's domain
$Searcher = New-Object DirectoryServices.DirectorySearcher
$Searcher.SearchScope = "subtree"
$Searcher.Filter = $Filter
$Searcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$($RootOU)")
$Searcher.FindAll()|sort | foreach { $hash[($_.Properties.ncname).Trim()] = ($_.Properties.cn).Trim() }
$hash.GetEnumerator() | sort -Property Value
If the user details are available in $userDetails, then the you can get the correct domain with this:
$hash[[regex]::Match($userDetails.DistinguishedName, 'DC=.*').Value]
and the final username would look like this:
$hash[[regex]::Match($userDetails.DistinguishedName, 'DC=.*').Value] + "\" + $userDetails.SamAccountName

1) You can get the userPrincipalName from the DirectoryEntry.
2) Then, split the UPN up between the Username and Domain Name.
3) Then call GetNetBIOSName() on it.
public static DirectoryEntry GetDirectoryObject(string strPath)
{
if (strPath == "")
{
strPath = ConfigurationManager.AppSettings["LDAPPath"]; //YOUR DEFAULT LDAP PATH ie. LDAP://YourDomainServer
}
string username = ConfigurationManager.AppSettings["LDAPAccount"];
string password = ConfigurationManager.AppSettings["LDAPPassword"];
//You can encrypt and decrypt your password settings in web.config, but for the sake of simplicity, I've excluded the encryption code from this listing.
}
catch (Exception ex)
{
HttpContext.Current.Response.Write("user: " + username + ", LDAPAccount: "+ ConfigurationManager.AppSettings["LDAPAccount"] + ".<br /> "+ ex.Message +"<br />");
if (HttpContext.Current.User.Identity != null)
{
HttpContext.Current.Response.Write("HttpContext.Current.User.Identity: " + HttpContext.Current.User.Identity.Name + ", " + HttpContext.Current.User.Identity.IsAuthenticated.ToString() + "<br />");
HttpContext.Current.Response.Write("Windows Identity: " + WindowsIdentity.GetCurrent().Name + ", " + HttpContext.Current.User.Identity.IsAuthenticated.ToString());
}
else
{
HttpContext.Current.Response.Write("User.Identity is null.");
}
HttpContext.Current.Response.End();
}
DirectoryEntry oDE = new DirectoryEntry(strPath, username, password, AuthenticationTypes.Secure);
return oDE;
}
public static string GetNetBIOSName(string DomainName)
{
string netBIOSName = "";
DirectoryEntry rootDSE =GetDirectoryObject(
"LDAP://"+DomainName+"/rootDSE");
string domain = (string)rootDSE.Properties[
"defaultNamingContext"][0];
// netBIOSName += "Naming Context: " + domain + "<br />";
if (!String.IsNullOrEmpty(domain))
{
//This code assumes you have a directory entry at the /CN=Partitions, CN=Configuration
//It will not work if you do not have this entry.
DirectoryEntry parts = GetDirectoryObject(
"LDAP://"+DomainName+"/CN=Partitions, CN=Configuration," + domain);
foreach (DirectoryEntry part in parts.Children)
{
if ((string)part.Properties[
"nCName"][0] == domain)
{
netBIOSName += (string)part.Properties[
"NetBIOSName"][0];
break;
}
}
}
return netBIOSName;
}
public static string GetDomainUsernameFromUPN(string strUPN)
{
string DomainName;
string UserName;
if (strUPN.Contains("#"))
{
string[] ud = strUPN.Split('#');
strUPN= ud[0];
DomainName = ud[1];
DomainName=LDAPToolKit.GetNetBIOSName(DomainName);
UserName= DomainName + "\\" + strUPN;
}
else
{
UserName= strUPN;
}
return UserName;
}

Related

C#: Cannot set the Contact-Attribute "targetAddress" with my DirectoryEntry - Connection

I want to create a Contact-Object in a specific OU in our AD with some Attributes:
sn, givenName, mail, description, displayname, proxyAddresses and targetaddress.
I found lots of examples how to set the Attributes for a Contact-Object in Active Directory with C# and I`m able to create the Object with all Attributes except "targetaddress".
Could someone please Point me in the right Direction for this. Thank you!
public void CreateContact2(string Vorname, string Nachname, string EmailAdresse, string Beschreibung, string myDomainPath)
{
string CN = Vorname + " " + Nachname;
string mailNickName = EmailAdresse.Remove(EmailAdresse.IndexOf("#"));
string EmailAdresse2 = "SMTP:" + EmailAdresse;
DirectoryEntry directoryEntry;
try
{
directoryEntry = new DirectoryEntry("LDAP://myDomainPath")
directoryEntry.RefreshCache();
DirectoryEntry contact = directoryEntry.Children.Add("CN=" + CN, "Contact");
contact.Properties["sn"].Value = Nachname;
contact.Properties["givenName"].Value = Vorname;
contact.Properties["mail"].Value = EmailAdresse;
contact.Properties["description"].Value = Beschreibung;
contact.Properties["displayName"].Value = Nachname + ", " + Vorname;
contact.Properties["proxyAddresses"].Add(EmailAdresse2);
contact.Properties["targetaddress"].Value = EmailAdresse2;
contact.CommitChanges();
}
catch (Exception e)
{
// Do some error processing
var msg = e.Message.ToString();
Console.WriteLine("Fehler in Funktion CreateContact():" + msg);
}
}
The Contact-Object creates fine if I create it without the targetaddress-Attribute
But with it I´m
getting System.Runtime.InteropServices.COMException: “The specified
directory service attribute or value does not exist”
failure. Any Ideas?
I had he same issue , what i did is to restart and run Visual Studio as Administrator . It may be due to not having enough privileges to run some COM Methods
Thank you so much Guys!
I had found the answer by myself and . It was my mistake. The attribute wasn´t right delegated.

Adding group to local administrators

I am trying to add an existing group in Local Administrators. The group "ABC\Some Active Group" exists. I can add that through Windows GUI but I need to add it through code. Here is what I have tried so far:
public static bool AddGroup(string machineName, string groupName)
{
bool ifSuccessful = false;
try
{
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + machineName);
DirectoryEntry admGroup = localMachine.Children.Find("administrators", "group");
//admGroup.Children.Add(groupName, "Group");
admGroup.Invoke("Add", groupName);
admGroup.CommitChanges();
ifSuccessful = true;
}
catch (Exception ex)
{
ifSuccessful = false;
//logging
Console.WriteLine(machineName + " ----------" + ex.Message);
}
return ifSuccessful;
}
and I am calling it like:
AddGroup(Environment.MachineName, #"ABC\Some Active Group");
I get the exception, (its an inner exception)
An invalid directory pathname was passed
I also tried adding it like:
admGroup.Children.Add(groupName, "Group");
But then I got the exception:
The Active Directory object located at the path
WinNT://ABC/MachineName/Administrators is not a container
I have been able to successfully get all the users and groups with admGroup, I can't just add one. Can someone please tell me what am I doing wrong ?
You need to call AddGroup like this
AddGroup(Environment.MachineName, "WinNT://" + Environment.MachineName + "/Some Active Group");

C# script to connect to Oracle Directory Server Enterprise Edition

I have a C# script I am trying to use to connect to an Oracle Directory Server Enterprise Edition
So far i have had very little success.
I am receiving a Unknown error (0x80005000) error message
Can somebody tell me what I am doing wrong. I have been researching the web and most online boards say that this error message is because the LDAP in the path needs to be in uppercase letters.
As you can see I have done that but still no luck.
Below is my code
private static readonly string PATH = "LDAP://LDAPDEV.example.com:389/o=example.com";
private static readonly string USERNAME = uid=SERVICE_USR,ou=ApplicationIDs,o=example.com";
private static readonly string PASSWORD = "test1234";
string DN = "";
// connect to determine proper distinguishedname
DirectoryEntry Entry = new DirectoryEntry(Path, USERNAME, PASSWORD, AuthenticationTypes.None);
try
{
// Bind to the native AdsObject to force authentication.
Object obj = Entry.NativeObject;
DirectorySearcher Search = new DirectorySearcher(Entry);
Search.ReferralChasing = ReferralChasingOption.All
}
catch (Exception ex)
{
throw new Exception("Error looking up distinguishedname. ", ex);
}
finally
{
anonymousEntry.Close();
}
return DN;
}
string sDomain="LDAPDEV.example.com:389";
string sDefaultOU = #"o=example.com";
string sServiceUser = #"uid=user,ou=ApplicationIDs,o=example.com";
string sServicePassword = "password";
try
{
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain, sDomain, sDefaultOU, ontextOptions.SimpleBind, sServiceUser,sServicePassword);
}
catch (Exception ex)
{
ex.Message;
}
Here is something that you can use to get some information by using PrincipalContext look at this working example and replace the values with your domain name values.
// if you are doing this via web application if not you can replace
// the Request/ServerVariables["LOGON_USER"]
// with Environment.UserName; this will return your user name like msmith for example so var userName = Environvironment.UserName;
var userName = Request.ServerVariables["LOGON_USER"].Split(new string[] {"\\"}, StringSplitOptions.RemoveEmptyEntries);
var pc = new PrincipalContext(ContextType.Domain,"yourdomain.com",null, null);
var userFind = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, userName);
this is basically all you need to see the information like email address SamAccountName ect.. use the debugger to inspect all the property values available in the pc variable and userFind variable

Updating AD User information

I am having a problem updating user information in an Active Directory DB...
When I run the following code I get this error:
The specified directory service attribute or value does not exist
The problem is the path it is using to save the information is this:
CN=AD Test,OU=Container Name,DC=us,DC=flg,DC=int
Ad Test is the username in AD that I am trying to update.
and I believe it should be:
CN=Ad Test,OU=Container Name, OU=Server Name,DC=us,DC=flg,DC=int
I am new to Directory services so I would greatly appreciate any help in finding out why I cannot update... Thank you in advance
public bool UpdateActiveDirectory(string LdapServerName, string CustId, Employee SQLresult)
{
try
{
DirectoryEntry rootEntry = new DirectoryEntry("LDAP://" + LdapServerName, "usrename", "password", AuthenticationTypes.Secure);
DirectorySearcher searcher = new DirectorySearcher(rootEntry);
searcher.Filter = "(sAMAccountName=" + SQLresult.LogonNT + ")";
searcher.PropertiesToLoad.Add("title");
searcher.PropertiesToLoad.Add("street");
searcher.PropertiesToLoad.Add("1");
searcher.PropertiesToLoad.Add("st");
searcher.PropertiesToLoad.Add("postalCode");
searcher.PropertiesToLoad.Add("department");
searcher.PropertiesToLoad.Add("mail");
searcher.PropertiesToLoad.Add("manager");
searcher.PropertiesToLoad.Add("telephoneNumber");
SearchResult result = searcher.FindOne();
if (result != null)
{
// create new object from search result
DirectoryEntry entryToUpdate = result.GetDirectoryEntry();
entryToUpdate.Properties["title"].Value = SQLresult.Title;
entryToUpdate.Properties["street"].Value = SQLresult.Address;
entryToUpdate.Properties["1"].Value = SQLresult.City;
entryToUpdate.Properties["st"].Value = SQLresult.State;
entryToUpdate.Properties["postalCode"].Value = SQLresult.ZipCode;
entryToUpdate.Properties["department"].Value = SQLresult.Department;
entryToUpdate.Properties["mail"].Value = SQLresult.EMailID;
entryToUpdate.Properties["manager"].Value = SQLresult.ManagerName;
entryToUpdate.Properties["telephoneNumber"].Value = SQLresult.Phone;
entryToUpdate.CommitChanges();
Console.WriteLine("User Updated");
}
else
{
Console.WriteLine("User not found!");
}
}
catch (Exception e)
{
Console.WriteLine("Exception caught:\n\n" + e.ToString());
}
return true;
}
Maybe just a typo?
The third property you're trying to update:
entryToUpdate.Properties["1"].Value = SQLresult.City;
is that a one (1) in there? It should be a small L (l) instead.
Also: the manager's name must be the Distinguished Name of the manager - the whole
CN=Manager,CN=Ad Test,OU=Container Name, OU=Server Name,DC=us,DC=flg,DC=int
thing - not just the name itself.
If that doesn't help anything - just go back to old-school debugging technique:
update just a single property; if it fails --> that's your problem case - figure out why it's a problem.
If it works: uncomment a second property and run again
-> repeat over and over again, until you find your culprit

Full name rather than the domain id in User.Identity.Name

The User.Identity.Name property returns the domain login id.
Which class/property exposes the actual user name?
For user "John Doe" who logs into the web application supplying my_domain\jdoe
**User.Identity.Name -**
Returns : *my_domain\jdoe*
**System.Environment.UserName**
Returns: *jdoe*
Which class/property returns? ... "John Doe"
If you are thinking Active Directory, you'll need to find the UserPrincipal that corresponds to the given samAccountName and get the DisplayName property from it. Note that it may not be set.
string fullName = null;
using (PrincipalContext context = new PrincipalContext( ContextType.Domain ))
{
using (UserPrincipal user
= UserPrincipal.FindByIdentity( context,
User.Identity.Name ))
{
if (user != null)
{
fullName = user.DisplayName;
}
}
}
Sounds like instead of the login name, you are after the display name of an Active Directory user account. What you might want to do is to do an AD search (DirectorySearcher) and get the display name from the search result property.
I'm assuming that you are in an AD environment, since you tagged the question adsi.
Note: If you are working with .NET 3.5, you might want to look at tvanfosson's post.
The IIdentity interface is that which provides the Name property on User.Identity. The IIdentity interface can be implemented on any number of classes which know how to lookup users from a data-store (SQL Server, Active Directory, etc).
There is no property of the IIdentity interface which provides "John Doe". If that information is located in your data-store then you'll need to use the tools specific to that data-store to access it.
That said, its entirely possible that the object which is returned by User.Identity has a property which contains "John Doe" that you might be able to access through some other interface besides IIdentity (our custom IIdentity implementation does this, for example).
using System.DirectoryServices;
public static string GetFullName(string strLogin)
{
string str = "";
string strDomain;
string strName;
// Parse the string to check if domain name is present.
int idx = strLogin.IndexOf('\\');
if (idx == -1)
{
idx = strLogin.IndexOf('#');
}
if (idx != -1)
{
strDomain = strLogin.Substring(0, idx);
strName = strLogin.Substring(idx + 1);
}
else
{
strDomain = Environment.MachineName;
strName = strLogin;
}
DirectoryEntry obDirEntry = null;
try
{
obDirEntry = new DirectoryEntry("WinNT://" + strDomain + "/" + strName);
System.DirectoryServices.PropertyCollection coll = obDirEntry.Properties;
object obVal = coll["FullName"].Value;
str = obVal.ToString();
}
catch (Exception ex)
{
str = ex.Message;
}
return str;
}
and the you can just call
var strJonDoeName = GetFullName(User.Identity.Name)
code mock it from here
Maybe I have made a mistake somewhere, but WinNT://... hasn't worked for domain accounts for me. Additionally, if the user isn't in the same domain as the machine, than PrincipalContext may not find the wanted element (as it searches in the current context, if used as above).
The following should translate the "friendly domain name" as provided by User.Identity.Name to an ldap compliant domain. Using the distinguishName of the domain delivers the needed ldap path information (which has solved my cross domain problem):
(VB.NET, sorry)
Imports System.DirectoryServices
Imports System.DirectoryServices.AccountManagement
Imports System.DirectoryServices.ActiveDirectory
...
Dim strUserName As String
Dim objDirContext As DirectoryContext
Dim objContext As PrincipalContext
Dim objPrincipal As Principal
Dim strLDAPDomainPath As String
...
// User.Identity.Name delivers domain\account or account#domain
// Split User.Identity.Name in domain and account as specified above
strDomain = "my_domain"
strAccount = "jdoe"
// Get LDAP domain relative path (distinguishName) from short domain name (e.g. my_domain -> us.my_domain.com -> DC=us,DC=my_domain,DC=com)
Try
objDirContext = New DirectoryContext(DirectoryContextType.Domain, strDomain)
strLDAPDomainPath = Domain.GetDomain(objDirContext).GetDirectoryEntry.Properties("distinguishedName").Value.ToString
Catch objException As DirectoryServicesCOMException
Throw New Exception("Couldn't get LDAP domain: " & objException.Message)
End Try
// Find user in LDAP
// Nothing results in using current domain controller
Try
objContext = New PrincipalContext(ContextType.Domain, Nothing, strLDAPDomainPath)
objPrincipal = Principal.FindByIdentity(objContext, IdentityType.Name, strAccount)
If Not IsNothing(objPrincipal) Then
strUserName = objPrincipal.DisplayName
End If
Catch objException As Exception
Throw New Exception("Couldn't get user display name: " & objException.Message)
End Try
// strUserName should now contain the wanted full name

Categories