Monday, April 7, 2014

How to find a user's effective rights on a file

A client wanted a handler to figure out the current user's effective rights. I could not impersonate the user, so we needed to check the user's rights. Here is the class I am using to do it.



 public static class FileSystemRightsEx
    {
        public static bool HasRights(this FileSystemRights rights, FileSystemRights testRights)
        {
            return (rights & testRights) == testRights;
        }
    }


    public static class FileSystemEffectiveRights
    {
        public static FileSystemRights GetRights(string userName, string path)
        {
            ULSLoggingService.LogUITrace("Trying to get rights for the path.");

            if (string.IsNullOrEmpty(userName))
            {
                throw new ArgumentException("userName");
            }

            if (!Directory.Exists(path) && !File.Exists(path))
            {
                throw new ArgumentException(string.Format("path:  {0}", path));
            }

            return GetEffectiveRights(userName, path);
        }



        /// <summary>
        /// based on the rules retrieved figure out if the user has acces
        /// </summary>
        /// <param name="userName">user name no domain</param>
        /// <param name="path">file share path</param>
        /// <returns></returns>
        private static FileSystemRights GetEffectiveRights(string userName, string path)
        {
            FileSystemAccessRule[] accessRules = GetAccessRulesArray(userName, path);
            FileSystemRights denyRights = 0;
            FileSystemRights allowRights = 0;

            for (int index = 0, total = accessRules.Length; index < total; index++)
            {
                FileSystemAccessRule rule = accessRules[index];

                if (rule.AccessControlType == AccessControlType.Deny)
                {
                    denyRights |= rule.FileSystemRights;
                }
                else
                {
                    allowRights |= rule.FileSystemRights;
                }
            }

            return (allowRights | denyRights) ^ denyRights;
        }

        /// <summary>
        /// Compare the file system access rules with the sids comming from the user
        /// if we might have a deny rule or an allow rule
        /// </summary>
        /// <param name="userName">user name without domain</param>
        /// <param name="path">path to file</param>
        /// <returns></returns>
        private static FileSystemAccessRule[] GetAccessRulesArray(string userName, string path)
        {
            ULSLoggingService.LogUITrace(string.Format("Trying to get access rules array for user '{0}' and path '{1}'.", userName, path));

            // get all access rules for the path - this works for a directory path as well as a file path
            AuthorizationRuleCollection authorizationRules = (new FileInfo(path)).GetAccessControl().GetAccessRules(true, true, typeof(SecurityIdentifier));

            foreach (AuthorizationRule rule in authorizationRules)
            {
                ULSLoggingService.LogUITrace(string.Format("FileSystem Rule name: '{0}'",rule.IdentityReference.Translate(typeof(NTAccount)).Value));
            }

            // get the user's sids
            string[] sids = GetSecurityIdentifierArray(userName);

            // get the access rules filtered by the user's sids
            return (from rule in authorizationRules.Cast<FileSystemAccessRule>()
                    where sids.Contains(rule.IdentityReference.Value)
                    select rule).ToArray();
        }

        /// <summary>
        /// Get the group SIDS of the current user
        /// assumption: that users are unique within the domain
        /// </summary>
        /// <param name="userName">user's name without the domain</param>
        /// <returns>array of sids</returns>
        private static string[] GetSecurityIdentifierArray(string userName)
        {
            // connect to the domain
            PrincipalContext pc = new PrincipalContext(ContextType.Domain);

            // search for the domain user
            UserPrincipal user = new UserPrincipal(pc) { SamAccountName = userName };
            PrincipalSearcher searcher = new PrincipalSearcher { QueryFilter = user };
            user = searcher.FindOne() as UserPrincipal;

            if (user == null)
            {
                throw new ApplicationException(string.Format("Invalid User Name:  {0}", userName));
            }

            // use WindowsIdentity to get the user's groups
            WindowsIdentity windowsIdentity = new WindowsIdentity(user.UserPrincipalName);
            string[] sids = new string[windowsIdentity.Groups.Count + 1];

            sids[0] = windowsIdentity.User.Value;

            for (int index = 1, total = windowsIdentity.Groups.Count; index < total; index++)
            {
                sids[index] = windowsIdentity.Groups[index].Value;
                try
                {
                    ULSLoggingService.LogUITrace("User:" + userName + "User Group:" + windowsIdentity.Groups[index].Translate(typeof(NTAccount)).Value);
                }
                catch (Exception ex)
                {
                    ULSLoggingService.LogUIException("proplem with logging", ex);
                }
            }

            return sids;
        }
    }

No comments:

Post a Comment