Get WindowsPrincipal from UserPrincipal - c#

The goal
I'm writing a class that abstracts various Windows user mechanics. My class knows about the user's account name and domain, if any. I am trying to hydrate a property that indicates whether the user has administrative privilege on either the domain or the local environment that it belongs to.
The problem
The WindowsPrincipal class provides that information via IsInRole, but it's constructor requires a WindowsIdentity, which I can't find a way to establish without a user principal name (UPN). The UserPrincipal.UserPrincipalName property is available for domain users, but null for local users. Is there another way to get a WindowsPrincipal from a UserPrincipal? Alternatively, is there another way to accomplish the goal without it?
The source
using (PrincipalContext principalContext = new PrincipalContext(PrincipalContextType, principalContextName))
{
using (UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(principalContext, IdentityType.Name, Name))
{
// Capture additional information from the user principal.
Certificates = userPrincipal.Certificates;
DisplayName = userPrincipal.DisplayName;
UserPrincipalName = userPrincipal.UserPrincipalName;
// This constructor blows up because UserPrincipalName is null for local users.
using (WindowsIdentity windowsIdentity = new WindowsIdentity(UserPrincipalName))
{
// Capture group membership information about the specified user.
WindowsPrincipal windowsPrincipal = new WindowsPrincipal(windowsIdentity);
// Determine if the user has administrative privilege on the domain or local machine.
HasAdministrativePrivilege = windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
}
}
}
Thanks in advance.

Related

Changing AD User Account Property by using the UserPrincipal

I am trying to change the User Account Property in Active Directory by using the UserPrincipal.
I have read that we have to use the special account which has the write access to the Active Directory rather than the current log on user. So, I created the special class to impersonate by using the Special Account. But I am still having the
System.UnauthorizedAccessException: General access denied error
at user.Save(ctx); line.
System.Security.Principal.WindowsImpersonationContext newUser = clsImpersonate.ImpersonateUser("ADUser", "ADPassword");
if (newUser != null)
{
PrincipalContext ctx = blAD.GetAdminPrincipalContext();
UserPrincipal user = blAD.GetUserPrincipal(this.SAMAccount);
user.Enabled = false;
user.Save(ctx);
newUser.Undo();
}
How can I achieve this requirement? Thanks.
What permissions have been delegated to your special user? It needs to be able to write userAccountControl on the users in question.
I wouldn't impersonate the account first off! Gain access through by passing the values through ad first.
For the real issue, look at the error:
Get the principalContect.
Get the userprincipal.
Do what you want to do.
Save it, why are u using undo? Delete the Undo().
To access the Principle as another user, define your PrincipalContext with the credentials of the user and use that PrincipalContext when getting the UserPrincipal.
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "domain.tld", "ADUser", "ADPassword");
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, this.SAMAccount);
if (user != null)
{
user.Enabled = false;
user.Save();
}
If you are still getting the UnauthorizedAccess Exception, it is likely because the account you are specifying does not have access to write the userAccountControl attribute on the user object in Active Directory/LDS.

How to edit the registry keys of a specific user programatically?

I want to change a few settings of a Windows user that I created in my application. If I understand correctly, his "HKEY_CURRENT_USER" values will be under HKEY_USERS/<sid>/.... Is this correct? How can I get the sid of the user, if I know the user name and the domain?
Edit: How can I correctly edit the HKCU keys of that user, if I have the sid already?
I have a program that does exactly that. Here is the relevant part of the code:
NTAccount ntuser = new NTAccount(strUser);
SecurityIdentifier sID = (SecurityIdentifier) ntuser.Translate(typeof(SecurityIdentifier));
strSID = sID.ToString();
You will need to import two namespaces:
using System.DirectoryServices;
using System.Security.Principal;
Hope this helps.
Then use Registry.Users.SetValue with SID string\path to set the registry value.
This might not work as intended if you are editing a logged-off profile, especially a roaming profile.
There are two steps to this. First you must get the users sid. Second you must load the users registry hive. Other users hives are not loaded by default so you must load it explicitly.
The answer in Daniel White's comment is the best way to get the sid.
To load the user's registry hive, use the LoadUserProfile windows API via pinvoke. There is a complementary UnloadUserProfile to unload the hive when you are done with it.
You can use Query by example and search using PrincipalSearcher for appropriate UserPrincipal
// Since you know the domain and user
PrincipalContext context = new PrincipalContext(ContextType.Domain);
// Create the principal user object from the context
UserPrincipal usr = new UserPrincipal(context);
usr .GivenName = "Jim";
usr .Surname = "Daly";
// Create a PrincipalSearcher object.
PrincipalSearcher ps = new PrincipalSearcher(usr);
PrincipalSearchResult<Principal> results = ps.FindAll();
foreach (UserPrincipal user in results) {
if(user.DisplayName == userName) {
var usersSid = user.Sid.ToString();
}
}

ASP.NET Active Directory C# field specification

We've got an active directory here. provided the unique user id of the user, I need to access the organization->manager->name attribute related to that userid. Basically this will be used to send an approval form to the manager of the person submitting request.
Any idea how this could be done?
You can use the following code :
/* Retreiving object from SID
*/
string SidLDAPURLForm = "LDAP://WM2008R2ENT:389/<SID={0}>";
System.Security.Principal.SecurityIdentifier sidToFind = new System.Security.Principal.SecurityIdentifier("S-1-5-21-3115856885-816991240-3296679909-1106");
/*
System.Security.Principal.NTAccount user = new System.Security.Principal.NTAccount("SomeUsername");
System.Security.Principal.SecurityIdentifier sidToFind = user.Translate(System.Security.Principal.SecurityIdentifier)
*/
DirectoryEntry userEntry = new DirectoryEntry(string.Format(SidLDAPURLForm, sidToFind.Value));
string managerDn = userEntry.Properties["manager"].Value.ToString();
But you can also find in this post other ways to seach bind to Active-directory.
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
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!
I'm not 100% sure what you want to do in your concrete case... the UserPrincipal has an EmployeeId property - is that what you want to search for?
Use the System.DirectoryServices.DirectoryEntry class to read out the appropriate property of the user object. The constructor of DirectoryEntry requires that you have an LDAP path to the user. Getting the LDAP path can often be tricky though as IIS prefers handing over the SAM account name only. If you provide more details of what the user id you have looks like it is easier to point you in the right direction.
To do this the account which runs the ASP.NET application needs read access to the AD, which probably doesn't have by default. Changing the application pool to run under "NetworkService" is the easiest way if the web server belongs to the AD. The ASP.NET app will then use the MACHINE$ account of the server to access the AD.

AD Update email address using MembershipUser - Access denied

I am trying to provision some users to update their email address in active directory (AD). I'm trying to achieve it using MembershipUser class. But getting 'general access denied error'. Here's my code:
string userName = "sathish";
System.Web.Security.MembershipUser userDetails = System.Web.Security.Membership.GetUser(userName);
if (userDetails != null)
{
userDetails.Email = "sathish#xyzee.com";
System.Web.Security.Membership.UpdateUser(userDetails); // getting access denied error here
}
My question is,
Do I need proper previleges to update email address to AD?
Do we have any attribute to verify my current access level?
Is it possible to impersonate privileges programmatically to update email address?
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
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context for your current, default domain
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find user by name
string userName = "sathish";
UserPrincipal user = UserPrincipal.FindByIdentity(userName );
// if user is found - update it's e-mail address and save
if(user != null)
{
user.EmailAddress = "sathish#xyzee.com";
user.Save();
}
The new S.DS.AM makes it really easy to play around with users and groups in AD:

What is the best way to get the value of what user is logged in?

I have a Windows form application that users can log into. The application is alone and doesn't connect with anything or anyone.
Besides creating a global variable, how could I have an easily accesible variable to check the current users permissions?
A not so kosher way of doing things is just pass the ID of the userType in the Form constructor and according to that, .Enable = false; buttons they don't have permissions to use.
Thanks!
If you want the id of the currently logged on Windows user (ie. the user that the application is running as), there are two ways of getting it:
By putting AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); in your startup, you can use Thread.CurrentPrincipal to get the user's security principal.
You can use WindowsIdentity.GetCurrent() to get the current user's identity. You can then create a security principal using new WindowsPrincipal(identity).
Both of these are equivalent, and will get you a security principal that has an IsInRole method that can be used to check permissions.
Use the WindowsIdentity class for getting the users Identity, found under System.Security.Principal.WindowsIdentity.
WindowsIdentity current = WindowsIdentity.GetCurrent();
Console.WriteLine("Name:" + current.Name);
Use the WindowsPrincipal class for getting the users Roles, found under System.Security.Principal.WindowsPrincipal.
WindowsIdentity current = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(current);
if (principal.IsInRole("your_role_here")
{
Console.WriteLine("Is a member of your role");
}

Categories