How to check if a user is also an administrator? - c#

I have a ClickOnce application and on startup I want to check if the user is an administrator.
When starting the application, I'm logged in as myself, and my user is member of the Administrators group. UAC is also turned off.
My first attempt was the following:
var principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
var admin = principal.IsInRole(WindowsBuiltInRole.Administrator);
The above didn't work. Going through all built-in roles, only WindowsBuiltInRole.User returns true. So the second attempt was the following:
var identity = WindowsIdentity.GetCurrent();
foreach (var role in identity.Groups)
{
if (role.IsValidTargetType(typeof(SecurityIdentifier)))
{
var sid = role.Translate(typeof(SecurityIdentifier)) as SecurityIdentifier;
if (sid.IsWellKnown(WellKnownSidType.AccountAdministratorSid) || sid.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid) )
{
admin = true;
break;
}
}
}
This second attempt didn't work either. admin stays false.
To further test the above, I created a new user and added him only to the Administrators group. He is not in the Users group. Starting the app as this new user will still return false.
The only time the above code will return true, ie. the user is an administrator, is when I log in and start the app and the pre-installed Administrator user account.
As I have UAC turned off I shouldn't be dealing with split tokens.
Is the problem related to the application being a ClickOnce app?
Is there a way (that actually works) which will return whether the user is actually an adminisatrator (note: the user can be part of the Domain Admins group but not a local admin)?

Related

Getting User Sid

I'm writing a service and I am trying to get the logged in User's sid and for whatever reason it is not working. It only returns {S-1-5-18}. Yet if I create a quick console application, it works just fine.
I've tried 2 methods:
WindowsIdentity usr = WindowsIdentity.GetCurrent();
return usr.User
as well as:
UserPrincipal.Current.Sid
They both have the same affect in my service. They both only return {S-1-5-18}. Yet in a console app, they both return the full user sid.
What could be causing this?
I suppose you are running your service-process as NT AUTHORITY\SYSTEM or .\LOCALSYSTEM.
Please see KB 243330 for more detail:
SID: S-1-5-18
Name: Local System
Description: A service account that is used by the operating system.
If you want to get the SID from the desktop-session, you could eg go for (by utilizing cassia - nuget-package available) :
ITerminalServicesSession GetActiveSession()
{
var terminalServicesSession = default(ITerminalServicesSession);
var terminalServicesManager = new TerminalServicesManager();
using (var server = terminalServicesManager.GetLocalServer())
{
foreach (var session in server.GetSessions())
{
if (session.ConnectionState == ConnectionState.Active)
{
// yep, I know ... LINQ ... but this is from a plain .NET 2.0 source ...
terminalServicesSession = session;
break;
}
}
}
return terminalServicesSession;
}
The ITerminalServiceSession-instance does contain the property SessionId which should work as needed. But please, be aware that there are caveats associated with state of the session - I do not guarantee that my condition suffices, you may need to adapt the condition on ConnectionState as needed.
Those APIs will return the SID of the user executing the current process, in your case your service. S-1-5-18 is NT AUTHORITY\SYSTEM.
There can be anything from zero to many users logged on to a Windows system (for interactive use: either locally or remotely): there is no singular "logged on user".
You need to refine your requirements: why do you want to know the logged on user?

c# application authorization based on Windows current user

I write a small application and I need to add some authorization security tips.
Simple Example:
I have a WinForm that contain two buttons: btnAdd and btnDelete. I want that:
if the current user is in the Administrator group, both the two buttons above will be displayed
else (if not in the Administrator group (like like guess account)) only btnAdd will be displayed.
How can I do that
You should be able to construct a WindowsPrincipal object, then just check if the user is in the role you expect, and use the return value to set the button to visible or not. Something like the below
using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
{
WindowsPrincipal principal = new WindowsPrincipal(identity);
// using WindowsBuiltInRole.Administrator or "BUILTIN\\Administrators" should work
btnAdd.Visible = principal.IsInRole(WindowsBuiltInRole.Administrator);
}
The updated code works, I've tested this, where I'm a local admin, since you want to test the current user, WindowsIdentity.GetCurrent() is actually cleaner.
If you need to confirm whether or not you are an admin on the machine, you can either check the group membership through MMC, but if there is any domain groups involved and you don't have access you won't be able to check this through MMC unless you have access to AD.
A sure way to verify if a user is a member of group is running the below in a command prompt window
Get members of local Administrators group:
net localgroup administrators
Get members of the domain group: this is necessary if for example, mydomain\WorkstationAdmins is a member of the local Administrators group and userId is a member of mydomain\WorkstationAdmins (thus an admin of the wokstation)
net group "WorkstationAdmins" /domain

Why UserPrincipal.Enabled returns different values?

I am trying to determine if a user account in AD is enabled. For this I use the following code:
string domain = "my domain";
string group = "my security group";
string ou = "my OU";
//init context
using (var cnt= new PrincipalContext(ContextType.Domain, domain))
{
//find the necessary security group
using (GroupPrincipal mainGroup
= GroupPrincipal.FindByIdentity(cnt, IdentityType.Guid, group))
{
if (mainGroup != null)
{
//get the group's members
foreach (var user in mainGroup.GetMembers()
.OfType<UserPrincipal>()
.Where(u => u.DistinguishedName.Contains(ou)))
{
//ensure that all the info about the account is loaded
//by using FindByIdentity as opposed to GetMembers
var tmpUser= UserPrincipal.FindByIdentity(cnt,
user.SamAccountName);
//actually I could use `user` variable,
//as it gave the same result as `tmpUser`.
//print the account info
Console.WriteLine(tmpUser.Name + "\t" +
tmpUser.Enabled.HasValue + "\t" +
tmpUser.Enabled.Value);
}
}
}
}
The problem is, when I run this code under an administrative account, I get the real result, while when I run it under a non-priviledged account, user.Enabled returns false for some of the accounts, while it should be true.
The only similar q&a I managed to find are
UserPrincipal.Enabled returns False for accounts that are in fact enabled?
Everything in Active Directory via C#.NET 3.5 (Using System.DirectoryServices.AccountManagement)
which do not help here.
Why is that so? What are my options to get this info under a non-priviledged account?
Here is another approach: How to determine if user account is enabled or disabled:
private bool IsActive(DirectoryEntry de)
{
if (de.NativeGuid == null)
return false;
int flags = (int)de.Properties["userAccountControl"].Value;
if (!Convert.ToBoolean(flags & 0x0002))
return true;
else
return false;
}
Same approach is described in Active Directory Objects and C#.
However when running under an unpriviledged user account, userAccountControl attribute is null and it's not possible to determine the state of the account.
The workaround here is to use PrincipalContext Constructor, specifying the credentials of a user with enough priviledges to access AD.
It stays unclear to me, why the unpriviledged user had access to AD at all, and couldn't get values of some certain account attributes. Probably this has nothing to do with C#, and should be configured in AD...
You'll need to delegate permissions in Active Directory for the accounts that will be performing the AD queries. This is what I had to do for my applications to work (though we are performing other administrative tasks on user accounts).
Check Here for instructions on how to delegate permissions(or see blockquote below).
You may referred the following procedure to run the delegation:
Start the delegation of control wizard by performing the following steps:
Open Active Directory Users and Computers.
In the console tree, double click the domain node.
In the details menu, right click the organizational unit, click delegate control, and click next.
Select the users or group to which you want to delegate common administrative tasks. To do so, perform the following steps:
On the Users or Groups page, click Add.
In the select Users, computers or Groups, write the names of the users and groups to which you have to delegate control of the organizational unit, click OK. And click next.
Assign common tasks to delegate. To do so perform the following common tasks.
On the tasks to delgate page, click delegate the following common tasks.
On the tasks to delegate page, select the tasks you want to delegate, and click OK. Click Finish
For Example: To delegate administrator to move user/computer objects, you can use advance mode in AD User and Computer and run delegation. It should have write privilege in both OU for the object moving. For writing new values, the administrators account should have delegated values on the user account (Full privilege in specific OU as well.
Something else worth looking into is if the accounts have the userAccountControl attribute. I've heard that accounts missing this attribute may not report correctly. In most scenarios this attribute should be set to NormalAccount.

How to test if a user is in the Built-in Administrators group without elevating privileges?

I expected this code:
WindowsPrincipal principal = new WindowsPrincipal( WindowsIdentity.GetCurrent() );
public bool UserHasAdminRights( WindowsPrincipal principal, WindowsBuiltInRole role )
{
bool isAdmin;
// get the role information of the current user
if ( principal.IsInRole( role ) )
{
isAdmin = true;
}
else
{
isAdmin = false;
}
return isAdmin;
}
to return true when a user is in the Built-in Administrators group.
HOWEVER
MSDN for IsInRole states:
In Windows Vista, User Account Control (UAC) determines the privileges
of a user. If you are a member of the Built-in Administrators group,
you are assigned two run-time access tokens: a standard user access
token and an administrator access token. By default, you are in the
standard user role. When you attempt to perform a task that requires
administrative privileges, you can dynamically elevate your role by
using the Consent dialog box. The code that executes the IsInRole
method does not display the Consent dialog box. The code returns false
if you are in the standard user role, even if you are in the Built-in
Administrators group. You can elevate your privileges before you
execute the code by right-clicking the application icon and indicating
that you want to run as an administrator.
Question is how do I modify this code so that it returns true, if the user is in the built-in Admin group, WITHOUT requiring the user to elevate permissions during/before runtime?
Since I can't neatly post code in comments, like I can here, let me just suggest some pseudo code
var user = Windows.GetThisUser( me ); //current method (I said pseudo code)
function CheckIfImAdmin( user ) .... //current method
proposed method:
var administrativeUsers = Windows.GetUsersInRole( "admin" ); //use a SID here, much more reliable
foreach(user in administrativeUsers){
if (user == me) return true;
return false;
}
While this may look the same, it's not. Instead of querying the user to see if it's currently in a given role (non-escalated aren't) I'm focusing on who the administrators are and then asking if that group contains the user I want, namely the current user.

Enabling the button visibility for different accounts

I'm currently working on a website that requires me to enable validation for different accounts.
I have 2 accounts - Admin and User.
My Admin account is able to view all the functions available in the website, but the User is only allowed a few functions.
What I have in mind is to disable the visibility of a button - btnUpload.
Below are the codes that I've came up with so far.
Mp.Mp login = new Mp.Mp();
bool result = login.AuthenticateUser(tbxUsername.Text, tbxPassword.Text);
if (result == true) {
Session.Add("Session_name", tbxUsername.Text);
//Session["Username"] = tbxUsername.Text;
Response.Redirect("Index.aspx");
}
I need help with the visibility of buttons to ensure that btnUpload appears only to Admin and not User.
May be you want some thing like this
if(Session["Session_name"] == "admin")
{
btnUpload.Visible = true;
}
else
{
btnUpload.Visible = false;
}
You have RollName AND RollID For Identify your user by that roleid.
You just need to check rollID at time of user login and store your user rollid in session and then check this rollid in whatever page where you need to check constrain regarding user role.
try
btnUpload.Visible = Session ["Session_name"] == "Admin";
How you are going to decide if the user is admin or a regular user? In simplistic scenario, your user data may have a flag that indicates whether user is an admin or not. Flexible approaches uses role based security - so if the user is member of particular role then he get access to certain features. So in such system, you can have a access right (or feature) for admin privileges and admin role will have this feature. In integrated authentication schemes, you can also use on windows groups or active directory groups as a security role. ASP.NET has good support for role based security (see this, this and this). Regardless of a implementation, once you decide is user is admin or not, controlling UI is very simple thing.

Categories

Resources