简体   繁体   中英

Faster method to test if (not current) user is an administrator

I need to test which Windows users have administrator privileges.

OK, now read carefully: NOT CURRENT USER . I query all local user accounts, then I test which one of them has administrator privileges. Let's say I'm logged as Joe, my application runs in Joe user's context, but there is a user Timmy on this very PC, who is not currently logged on. I need to test if Timmy has admin on this PC. So, this question is definitely not about current user privileges ;) So, this is definitely not a duplicate of similar questions about determining the privileges of the current user. This one is different ;)

Here's my code:

public static dynamic[] Users => WMI.Query("SELECT * FROM Win32_UserAccount WHERE Disabled = 0").Select<dynamic, dynamic>(d => {
    var machineContext = new PrincipalContext(ContextType.Machine);
    Principal principal = Principal.FindByIdentity(machineContext, d.SID);
    d.IsAdmin = principal.IsMemberOf(machineContext, IdentityType.Name, "Administrators");
    principal.Dispose();
    machineContext.Dispose();
    return d;
}).ToArray();

This works, but it takes more than 2 seconds to execute IsMemberOf() . Is there a faster way to do this? Why is it so slow?

If you wonder what WMI.Query does here, it just queries the WMI and returns result as an array of managed dynamic objects instead of IDisposable types. IDisposable types are disposed before the result is returned. Irrelevant to the question, though.

To clarify, I use System.DirectoryServices.AccountManagement to get an actual user account from SID. I don't know if WindowsIdentity can be created from SID. AFAIK it can't. The user for WindowsIdentity needs to be logged on (throws a SecurityException if not), and I query all local users, not just the current one.

Well, I found it out, however it's still weird...

Updated code: (I changed matching group name to matching group SID).

public static dynamic[] Users => WMI.Query("SELECT * FROM Win32_UserAccount WHERE Disabled = 0").Select<dynamic, dynamic>(d => {
    using (var machineContext = new PrincipalContext(ContextType.Machine))
    using (Principal principal = Principal.FindByIdentity(machineContext, d.SID))
    d.IsAdmin = principal.GetGroups().Any(i => i.Sid.IsWellKnown(System.Security.Principal.WellKnownSidType.BuiltinAdministratorsSid));
    return d;
}).ToArray();

It turns out GetGroups() is way faster than IsMemberOf() .

Update: It's actually roughly 135 times faster. GetGroups() with Any() took 17ms instead of 2300ms IsMemberOf() took.

As a bonus I'll share with my WMI.Query ;)

/// <summary>
/// Safe, managed WMI queries support.
/// </summary>
static class WMI {

/// <summary>
/// Queries WMI and returns results as an array of dynamic objects.
/// </summary>
/// <param name="q"></param>
/// <returns></returns>
public static dynamic[] Query(string q) {
    using (var s = new ManagementObjectSearcher(q))
        return
            s
            .Get()
            .OfType<ManagementObject>()
            .Select(i => {
                var x = new ExpandoObject();
                using (i) foreach (var p in i.Properties) (x as IDictionary<string, object>).Add(p.Name, p.Value);
                return x;
            })
            .ToArray();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM