I am working on a C# desktop application and i require so store some settings individually for each user in the HKCU section of the registry for each user. the requirements are such that these settings cannot be stored in a file or the database and registry is the best solution if possible..
Is it not possible to use impersonate method as we might not know passwords of all the users. however we will have the administrator access when running the exe.
is there a way that with administrator access we can access HKCU section for each user and populate the settings there
According to your scenario the first option was to impersonate the user, but as you are not willing to adopt it, now we have another option, obtain the user's SID, and then write the data under the KEY_USERS registry because this path contains all users in the machine.
Following is the utility function to write a value to specific user's registry key, obviously you need to know his/her username.
public static bool AddUserData(string userName, string key, string value)
{
try
{
//Gets the SID of the desired user
NTAccount f = new NTAccount(userName);
SecurityIdentifier s = (SecurityIdentifier)f.Translate(typeof(SecurityIdentifier));
string sid = s.ToString();
//Define the path to the subkey
string path = #"SOFTWARE\Console\DefaultConfig";
//try to open the path
var reg = Registry.Users.OpenSubKey(string.Format("{0}\\{1}", sid, path), true);
if (reg == null)
{
//if not exists create that path for that user
reg = Registry.Users.CreateSubKey(string.Format("{0}\\{1}", sid, path));
}
//set value against key
reg.SetValue(key, value);
return true;
}
catch
{
return false;
}
}
feel free to check documentation of the Registry class so that you will learn better to read the value back, this is just starting direction for you, Also I have not tested the code but I am pretty sure this will help you a lot. The admin access for the program is must for this code to work, otherwise you are left with impersonation.
I want to retrieve country, department, display name property from outlook.
Manually we are done using ctrl+k then right click outlook property.
If lots of records, then its time consuming process.
I have one mail id like var email="Something#domain.com"
Using
var a=outlook.Application.CreateReciepent("Email#hjg.com");
a.resolve();
var name=a.name;
able to fetch display name.
how to retrieve corresponding email id --country, department.
Please help me.
You will need to add System.DirectoryServices in the reference and userName in the code below is the NT Id (without domain). If you want to look for more properties you will need to search online for the exact string.
DirectorySearcher search = new DirectorySearcher();
// specify the search filter
search.Filter = "(&(objectClass=user)(anr=" + userName + "))";
// specify which property values to return in the search
search.PropertiesToLoad.Add("displayName"); // display name
search.PropertiesToLoad.Add("co"); // country name
search.PropertiesToLoad.Add("department"); // department
// perform the search
SearchResult result = search.FindOne();
Where can I find an example that does the following?
Pulls a user from Active Directory.
Gets the groups the user is a member of.
Gets a list of permissions assigned to each group.
This seems like a simple task but I can't find a solution.
The overall goal is to assign custom permissions and use them to control rights within an application.
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
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
if(user != null)
{
// do something here....
}
// 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!
The last point: permissions. Those aren't stored in Active Directory - and therefore, you can't retrieve those from any AD code.
Permissions are stored on the individual file system items, e.g. files and/or directories - or other objects (like registry keys, etc.). When you have an AD group or user account, you can read it's SID (Security Identifier) property - that SID will show up in ACL's (Access Control Lists) all over Windows - but from the user or group, there's no mechanism to get all permissions it might have anywhere in the machine/server.
Permissions for files and directories can e.g. be retrieved using the .GetAccessControl() method on the FileInfo and DirectoryInfo classes:
FileInfo info = new FileInfo(#"D:\test.txt");
FileSecurity fs = info.GetAccessControl();
DirectoryInfo dir = new DirectoryInfo(#"D:\test\");
DirectorySecurity ds = dir.GetAccessControl();
Deciphering and making sense of those is a whole different story altogether!
I'm struggling a bit with my simple console-dump-program. I connect to AD using
DirectoryEntry entry =
new DirectoryEntry("LDAP://" + domain, username, password);
and from there I recursively loop thru every child by
foreach (DirectoryEntry child in entry.Children)
{
Traverse(child);
}
Then I start getting mambo jambo data, users popping up more then once and null objects so I wonder if the way I handle the AD that it is just a handle and not a copy so its not loaded completely when I start traversing it?
Any tips/pointers on what to do?
If you can, move up to .NET 3.5 and use the new System.DirectoryServices.AccountManagement namespace - much easier to use.
See: Managing Directory Security Principals in the .NET Framework 3.5
Also: you need to understand Active Directory isn't just a flat list of users and groups - it's a hierachical system of OU's (organizational units) that can be nested into one another, and can contain users, groups, computers and more.
So what exactly do you want to do?? Get the users for a given OU (e.g. "Sales")?? Or really get all users from your AD?? You do understand this could take quite a while, depending on the size of your company's AD.......
If you really want to get ALL users and ALL groups from your entire AD - you should probably set up a DirectorySearcher at the root level:
// set search root
DirectoryEntry deRoot = new DirectoryEntry("LDAP://dc=YourCompany,dc=com");
// declare directory searcher
DirectorySearcher dsUsers = new DirectorySearcher(deRoot);
// scope is full subtree, filter defines to search for users
dsUsers.SearchScope = SearchScope.SubTree;
dsUsers.Filter = "(objectCategory=person)";
// define what properties you want to have loaded into your search results
dsUsers.PropertiesToLoad.Add("givenName");
dsUsers.PropertiesToLoad.Add("surname");
dsUsers.PropertiesToLoad.Add("samAccountName");
// loop through results of search
foreach(SearchResult result in dsUsers.FindAll())
{
if(result.Properties["givenName"] != null)
string givenName = result.Properties["givenName"][0].ToString();
if(result.Properties["surname"] != null)
string surname = result.Properties["surname"][0].ToString();
if(result.Properties["sAMAccountName"] != null)
string samAccountName = result.Properties["sAMAccountName"][0].ToString();
}
When reading out the properties of your SearchResult, you need to check to make sure you did actually get a value back - otherwise your assignment will crash and burn....
For the groups, just use this filter instead:
dsUsers.Filter = "(objectCategory=group)";
If you can narrow your search, e.g. to a given OU, you can get much better performance, since the search tree gets smaller and thus the search would be a lot faster. To do so, just define a different LDAP path for your deRoot directory entry (e.g. LDAP://OU=Sales,DC=YourCOmpany,DC=com or whatever OU you want to search in).
Update: as I said - with .NET 3.5, it gets a lot easier still! You need to add a reference to System.DirectoryServices.AccountManagement, and then you can use code something like this using a sort of "query-by-example" approach:
// create a domain context for the current domain
PrincipalContext domain = new PrincipalContext(ContextType.Domain);
// create a principal object decsribing what to search for
UserPrincipal user = new UserPrincipal(domain);
user.IsActive = true;
// create a principal searcher for running a search operation
PrincipalSearcher searcher = new PrincipalSearcher(user);
// run the query
PrincipalSearchResult<Principal> results = searcher.FindAll();
// iterate over all results
foreach (Principal result in results)
{
Console.WriteLine("name: {0}", result.Name);
}
And for searching for groups, just instantiate a GroupPrincipal, set any properties on it and then pass that into the PrincipalSearcher to search for groups.
I have a C# application that scans a directory and gathers some information. I would like to display the account name for each file. I can do this on the local system by getting the SID for the FileInfo object, and then doing:
string GetNameFromSID( SecurityIdentifier sid )
{
NTAccount ntAccount = (NTAccount)sid.Translate( typeof( NTAccount ) );
return ntAccount.ToString();
}
However, this does not work for files on a network, presumably because the Translate() function only works with local user accounts. I thought maybe I could do an LDAP lookup on the SID, so I tried the following:
string GetNameFromSID( SecurityIdentifier sid )
{
string str = "LDAP://<SID=" + sid.Value + ">";
DirectoryEntry dirEntry = new DirectoryEntry( str );
return dirEntry.Name;
}
This seems like it will work, in that the access to "dirEntry.Name" hangs for a few seconds, as if it is going off and querying the network, but then it throws a System.Runtime.InteropServices.COMException
Does anyone know how I can get the account name of an arbitrary file or SID? I don't know much about networking or LDAP or anything. There's a class called DirectorySearcher that maybe I'm supposed to use, but it wants a domain name, and I don't know how to get that either - all I have is the path to the directory I'm scanning.
See here for a good answer:
The best way to resolve display username by SID?
The gist of it is this bit:
string sid="S-1-5-21-789336058-507921405-854245398-9938";
string account = new System.Security.Principal.SecurityIdentifier(sid).Translate(typeof(System.Security.Principal.NTAccount)).ToString();
This approach works for me for non-local SID's over the active directory.
The SecurityReference object's Translate method does work on non-local SIDs but only for domain accounts. For accounts local to another machine or in a non-domain setup you would need to PInvoke the function LookupAccountSid specifying the specific machine name on which the look up needs to be performed.
System.DirectoryServices.AccountManagement.UserPrincipal class (msdn link) has a static function FindByIdentity to convert an SID to a User object. It should be able to work both against the local machine or an LDAP/Active Directory server. I have only used it against active directory.
Here is an example that I have used in IIS:
// Set the search context to a specific domain in active directory
var searchContext = new PrincipalContext(ContextType.Domain, "YOURDOMAIN", "OU=SomeOU,DC=YourCompany,DC=com");
// get the currently logged in user from IIS
MembershipUser aspUser = Membership.GetUser();
// get the SID of the user (stored in the SecurityIdentifier class)
var sid = aspUser.ProviderUserKey as System.Security.Principal.SecurityIdentifier;
// get the ActiveDirectory user object using the SID (sid.Value returns the SID in string form)
var adUser = UserPrincipal.FindByIdentity(searchContext, IdentityType.Sid, sid.Value);
// do stuff to user, look up group membership, etc.
In C#, get the user SID and assign it to a string variable through:
string strUser = System.Security.Principal.WindowsIdentity.GetCurrent().User.ToString();
You will need to use string because the ability to resolve to the UserName supports string. In other words, using var varUser will result in a namespace error.
string strUserName = new System.Security.Principal.SecurityIdentifier(strUser).Translate(typeof(System.Security.Principal.NTAccount)).ToString();
You can also get account name of special accounts like "Everyone" with code like this that will work regardless of user's language settings:
SecurityIdentifier everyoneSid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
string everyone = everyoneSid.Translate(typeof(System.Security.Principal.NTAccount)).ToString();
Ooh, then it's possible that the LDAP call is not working because you might not be in an Active Directory environment. If this is the case, then each of your machines is responsible for its own identity store. And your first code sample is not working across the network because the machine on which you are executing your code does not know how to resolve the SID that only makes sense on the remote machine.
You really should check if your machines are a part of an Active Directory. You would know this during the logon process. Or you can check by right clicking on "My Computer", select "Properties", the "Computer Name" tab, then see if your computer is part of a domain.
Great. I cribbed some LookupAccountSid() code from here:
http://www.pinvoke.net/default.aspx/advapi32.LookupAccountSid
And that worked, though I had to provide the host name myself. In the case of a UNC path I can just take the first component of it. When it's a mapped drive, I use this code to convert the path to a UNC one:
http://www.wiredprairie.us/blog/index.php/archives/22
It seems to work, so that's how I'll do it, unless someone comes up with a situation in which the first component of a UNC path isn't the host name...
Thank you all for your help.
This one is a stumper. You are in an Active Directory environment right? Just checking:)
Anyhow, instead of binding with sid.Value,
string str = "LDAP://<SID=" + sid.Value + ">";
I would try converting the SID's byte array to an Octet String and bind with that instead.
There is a sweet example here on page 78. This will get you closer. To be honest, I've not tried binding with a SID before. But I've had success binding with a user's GUID though :)
Good luck and let me know how it goes.
Get the current domain:
System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain();
Get a directory entry from ldap and the domain name:
DirectoryEntry de = new DirectoryEntry(string.Format("LDAP://{0}", domain));
Get the sid from an ActiveDirectoryMembershipProvider ActiveDirectoryMembershipUser:
ActiveDirectoryMembershipUser user = (ActiveDirectoryMembershipUser)Membership.GetUser();
var sid = (SecurityIdentifier)user.ProviderUserKey;
Get the username from the SecurityIdentifier:
(NTAccount)sid.Translate(typeof(NTAccount));
Get directory search done on an activedirectory with the domain directory entry and username:
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = string.Format("(SAMAccountName={0})", username);
search.PropertiesToLoad.Add("Name");
search.PropertiesToLoad.Add("displayName");
search.PropertiesToLoad.Add("company");
search.PropertiesToLoad.Add("homePhone");
search.PropertiesToLoad.Add("mail");
search.PropertiesToLoad.Add("givenName");
search.PropertiesToLoad.Add("lastLogon");
search.PropertiesToLoad.Add("userPrincipalName");
search.PropertiesToLoad.Add("st");
search.PropertiesToLoad.Add("sn");
search.PropertiesToLoad.Add("telephoneNumber");
search.PropertiesToLoad.Add("postalCode");
SearchResult result = search.FindOne();
if (result != null)
{
foreach (string key in result.Properties.PropertyNames)
{
// Each property contains a collection of its own
// that may contain multiple values
foreach (Object propValue in result.Properties[key])
{
outputString += key + " = " + propValue + ".<br/>";
}
}
}
Depending on the data in your active directory, you will get a varied response in the output.
Here is a site that has all the user properties I needed:
For all the Windows developers, the answer is LookupAccountSid
LookupAccountSid(null, Sid, username, userSize, domainName, domainSize, sidType);
I am quite sure you will be able to use the accepted answer from here: Determine the LocalSystem account name using C#
Basically, you can translate an instance of the SecurityIdentifier class to type NTAccount, from which you can get the user name. In code:
using System.Security.Principal;
SecurityIdentifier sid = new SecurityIdentifier("S-1-5-18");
NTAccount acct = (NTAccount)sid.Translate(typeof(NTAccount));
Console.WriteLine(acct.Value);