2

I have an In-house C# application that will be run on lap-tops in several remote locations. The most common users will have admin rights to the lap-tops, but sometimes it will be run by users without admin rights. For operational reasons, we want just one copy of the application per computer, so it will be installed under Program Files instead of the user accounts.

I am creating an auto-update routine. I would like it to have this behavior:

  1. It checks if there are any updates available.
  2. If there are updates and the user has no admin rights, They will be informed of the updates.
  3. If the user has admin rights, the updates will be loaded.
  4. In all cases, the application will be launched. Non-admin users can decide if the updates warrant shutting down and finding someone with admin rights.

99% of the time, there will be no updates, and I would prefer not to request privileges in the manifest when they usually will not be needed. So I plan on starting a separate process to actually load the updates. But in that case, I'd rather not bother non-admin users with requests for admin privileges that they cannot provide (no - they will not have another account they themselves can log into that has admin privileges).

Is there some reliable way I can have it determine - once it has found updates - whether the current user is in the administrators group, so that it will know whether to bother with launching the update process, or just report updates available and move on?

I've been searching for hours, but have only turned up one method (checking if the user has a split token) that is apparently unreliable and warned against.

Edit:

For completeness, the final solution I found based on Wheels73's post with corrections for the error I was getting is:

  bool CurrentUserIsAdmin() 
  {
     UserPrincipal user = UserPrincipal.Current;
     using (IEnumerator<Principal> groups = user.GetAuthorizationGroups().GetEnumerator()) 
     {
        while (groups.MoveNext()) 
        {
           try 
           {
              if (groups.Current.ToString() == "Administrators") 
              {
                 return true;
              }
           } 
           catch (NoMatchingPrincipalException) 
           {
              continue;
           }
        }
        return false;
     }
  }
Paul Sinclair
  • 223
  • 1
  • 12
  • Can you perhaps query the windows Active Directory Group to see what groups your users are in? – Wheels73 Apr 24 '17 at 13:19
  • I don't know. Can I? This is what I am asking. – Paul Sinclair Apr 24 '17 at 13:19
  • Is this the unreliable method you were talking about? http://stackoverflow.com/a/10045269/5226582 – user5226582 Apr 24 '17 at 13:21
  • @PaulSinclair - You can yes. I asked as not everyone uses AD. Add a ref to System.DirectoryServices. You create a new DirectorySearcher and pass in the users windows log in to search. – Wheels73 Apr 24 '17 at 13:23
  • @Wheels73 - thank you. I was a bit too brusk. I am not familiar with the Active Directory Group and started researching it with your comment. I do appreciate your advice. – Paul Sinclair Apr 24 '17 at 13:28
  • @user5226582 - yes. If you follow the linked blog, there is a link to another blog where this method apparently originated, and a later edit to that 2006 blog post warns against that method, though I am not well-enough versed in the issues to fully understand why. – Paul Sinclair Apr 24 '17 at 13:30
  • @PaulSinclair - Well if you want it.. I have a small function that takes a login id and returns you a list of AD group names. I'll post if it's useful to you? – Wheels73 Apr 24 '17 at 13:32
  • @Wheels73 - yes, it would be very useful. – Paul Sinclair Apr 24 '17 at 13:37

1 Answers1

2

As discussed, this is the routine I use to list all the AD directories for a given login.

 public List<string> GetUsersActiveDirectoryGroups(string windowsUserName)
 {
            var allUserGroups = new List<string>();
            var domainConnection = new DirectoryEntry();

            var samSearcher = new DirectorySearcher
            {
                SearchRoot = domainConnection,
                Filter = "(samAccountName=" + windowsUserName + ")"
            };
            samSearcher.PropertiesToLoad.Add("displayName");

            var samResult = samSearcher.FindOne();

            if (samResult == null) //User not found
                return allUserGroups;

            //Get groups
            var theUser = samResult.GetDirectoryEntry();
            theUser.RefreshCache(new[] {"tokenGroups"});

            foreach (byte[] resultBytes in theUser.Properties["tokenGroups"])
            {
                var mySid = new SecurityIdentifier(resultBytes, 0);

                var sidSearcher = new DirectorySearcher
                {
                    SearchRoot = domainConnection,
                    Filter = "(objectSid=" + mySid.Value + ")"
                };
                sidSearcher.PropertiesToLoad.Add("name");

                var sidResult = sidSearcher.FindOne();
                if (sidResult != null)
                {
                    allUserGroups.Add(sidResult.Properties["name"][0].ToString());
                }
            }

            return allUserGroups;
}

You could then check the contents of the groups to return a bool based upon the group name you are looking for.

 var myUsersGroups = GetUsersActiveDirectoryGroups("YOURLOGINNAME");
 var usersIsInAdmin = myUsersGroups.Any(g => g == "Administrator");

To detect if a user simply has loca admin rights, you can use the below

            WindowsIdentity user = null;

            user = WindowsIdentity.GetCurrent();
            var principal = new WindowsPrincipal(user);
            var isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);

OK.. final shout :)

To find out if another user has local admin rights, you can do the below

var usersPrincipal = UserPrincipal.FindByIdentity(UserPrincipal.Current.Context, IdentityType.SamAccountName, "YOURLOGINNAME");
var otherUserIsAdmin = usersPrincipal.GetAuthorizationGroups().Any(p => p.ToString() == "Administrators");

Hope that helps.

Wheels73
  • 2,850
  • 1
  • 11
  • 20
  • Thank you. But on testing, even though I can check and I do have administrative rights on my desktop, this does not list for me any group named even remotely like "Administrator". – Paul Sinclair Apr 25 '17 at 13:01
  • Hello.. Sorry..that was just an example. You will need to know the name of the group that gives your domain users the admin privileges you are looking for. – Wheels73 Apr 25 '17 at 13:12
  • The privileges in question are administrative rights to the local machine. To grant those rights to someone else, all I need to do is under "manage user accounts", select a user, open properties, and click on the "administrator" option. I do not have to assign them to some custom-named group. So it would appear that there is some standardized method of recognizing this. Perhaps local admin rights are not obtained through Active Directory Groups? – Paul Sinclair Apr 25 '17 at 13:35
  • Ah I see. I've added extra at the bottom of my answer. This should do what you need. – Wheels73 Apr 25 '17 at 13:45
  • No. I am already aware of that, but it only lells you if the current process is running with adminstrative rights. When the process is not running with elevated rights, it doesn't tell you if the user can successfully obtain those rights (with the current log-in) for a new process. My intent is to not bother users that cannot obtain those rights with an unnecessary request for them. – Paul Sinclair Apr 25 '17 at 13:51
  • I've made one last attempt :) That's all i've got. This should allow you to detect if another user has local admin rights by name. – Wheels73 Apr 25 '17 at 14:26
  • word of advise to future searchers: do not compromise on `IsUserAnAdmin()`. yes it may work, but it's deprecated and doesn't play well with the U.A.shit. on win 8 and beyond. – Stavm Apr 25 '17 at 14:54
  • @Stavm - a comment about a deprecated C++ function hardly seems apropo to this C# thread that makes no mention of it. – Paul Sinclair Apr 25 '17 at 15:24
  • @Wheels73 - That works on my desktop where I am an administrator. But the same test app crashes when I run it on a laptop where I am not an administrator. The crash appears to be caused by the `bool otherUserIsAdmin = usersPrincipal.GetAuthorizationGroups().Any(p => p.ToString() == "Administrators");` line, as it gives the marker line I put between the two. – Paul Sinclair Apr 25 '17 at 15:58
  • Okay - it turns out I get a "NoMatchingPrincipalException" while attempting to iterate through the groups. When I am an administrator, it stops before this error occurs. (I couldn't debug on the laptop as it doesn't have VS installed, so I didn't find it until I did a full iteration on the desktop). I'll track that down. Thanks again for the help. – Paul Sinclair Apr 25 '17 at 16:10
  • 1
    FYI - [this was the cause](http://stackoverflow.com/questions/16221974/getauthorizationgroups-is-throwing-exception), and the cure. – Paul Sinclair Apr 25 '17 at 18:07