8

我们有一个过程需要检查特定用户是否是本地管理员组的成员。

检查的代码如下所示:

using (PrincipalContext context = new PrincipalContext(ContextType.Machine, null))
{
    UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, sUserName);
    if (user != null)
    {
         SecurityIdentifier adminsGroupSID = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);
         GroupPrincipal group = GroupPrincipal.FindByIdentity(context, IdentityType.Sid, adminsGroupSID.Value);
         if (group != null)
         {
             if (user.IsMemberOf(group))
                 return 0;
         }
    }
}

当组中的帐户(例如域帐户)被删除时,我们会收到 PrincipalOperationException 和消息“枚举组成员身份时发生错误 (1332)。无法解析成员的 SID。”

有没有办法克服这个问题:a)从组中手动删除孤立的 SID b)不忽略它?

谢谢

4

5 回答 5

6

这很大程度上基于我在http://www.seirer.net/blog/2013/9/12/how-to-deal-with-localized-or-renamed-administrators-in-net上由 Michael Seirer 撰写的发现。他试图获取本地管理员帐户的 SID,而我们只需要该组中的名称。错误“无法解析成员的 SID”的原因。是因为 Active Directory 中不再识别某些帐户 - 可能是指向已删除用户帐户的遗物。您可以按照微软所说的去做,然后删除它们,并希望您的应用程序永远不会再次崩溃(尽管它会,下次删除该管理员组中的帐户时),或者使用我稍微修改的这段代码永久解决它麦克风。

using System.DirectoryServices;
using System.Collections;
using System.Runtime.InteropServices;

[DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool ConvertSidToStringSid(IntPtr pSid, out string strSid);

private static string GetTextualSID(DirectoryEntry objGroup)
{
    string sSID = string.Empty;
    byte[] SID = objGroup.Properties["objectSID"].Value as byte[];
    IntPtr sidPtr = Marshal.AllocHGlobal(SID.Length);
    sSID = "";
    System.Runtime.InteropServices.Marshal.Copy(SID, 0, sidPtr, SID.Length);
    ConvertSidToStringSid((IntPtr)sidPtr, out sSID);
    System.Runtime.InteropServices.Marshal.FreeHGlobal(sidPtr);
    return sSID; 
}


public static List<string> GetLocalAdministratorsNames()
{
    List<string> admins = new List<string>();
    DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + Environment.MachineName);
    string adminsSID = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null).ToString();

    string localizedAdmin = new System.Security.Principal.SecurityIdentifier(adminsSID).Translate(typeof(System.Security.Principal.NTAccount)).ToString();

    localizedAdmin = localizedAdmin.Replace(@"BUILTIN\", "");

    DirectoryEntry admGroup = localMachine.Children.Find(localizedAdmin, "group");
    object adminmembers = admGroup.Invoke("members", null);

    DirectoryEntry userGroup = localMachine.Children.Find("users", "group");
    object usermembers = userGroup.Invoke("members", null);

    //Retrieve each user name.
    foreach (object groupMember in (IEnumerable)adminmembers)
    {
        DirectoryEntry member = new DirectoryEntry(groupMember);

        string sidAsText = GetTextualSID(member);
        admins.Add(member.Name);            
    }
    return admins;
}

它将返回List<string>本地计算机上本地管理员组的成员。Environment.MachineName如果您不想要本地计算机,您甚至可以更改为域中的任何计算机名称。

然后您可以迭代列表以查看它们是否在其中:

private static bool isAdmin(string user)
{
    //string user = @"DOMAIN\doej";
    user = user.Split(@'\')[1];

    List<string> admins = GetLocalAdministratorsNames();
    foreach (string s in admins)
    {
        if (s == user)
            return true; // admin found
    }
    return false;  // not an admin
}
于 2017-08-17T23:22:43.037 回答
1

差不多十年后,即使在 .NET 4.x 和 5.x 中,这仍然是一个问题。解决此错误的另一种方法是从根本上解构foreach语句背后的代码并制作自己的搜索列表。您需要获取枚举器,然后在 Try/Catch 中调用 MoveNext。我最初担心如果它抛出异常它不会移动到下一个,但它确实如此,所以这行得通。它不漂亮,但我已经对其进行了测试,它对我有用。

PrincipalContext principalContext;
GroupPrincipal groupPrincipal;
UserPrincipal userPrincipal;
bool hasItem;

// `members` is the new list that can be searched without errors.
List<Principal> members = new List<Principal>();

using (principalContext = new PrincipalContext(ContextType.Machine))
{
    SecurityIdentifier adminsGroupSID = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);
    using (groupPrincipal = GroupPrincipal.FindByIdentity(principalContext, IdentityType.Sid, adminsGroupSID.Value))
    {
        /*
         * We will get our Enumerator. At this point, we are positioned before 
         * the current item, so we have to call MoveNext to get a valid Current
         */
        var e = groupPrincipal.Members.GetEnumerator();
        hasItem = false;

        do
        {
            try
            {
                /*
                 * Try and move next. If it failes, it will be caught by the catch
                 * and ignored. At which point, we can try and call MoveNext again
                 * and get to the next one.
                 */
                hasItem = e.MoveNext();
                if (hasItem)
                {
                    members.Add(e.Current);
                }
            }
            catch (PrincipalOperationException)
            {
                // We don't care about doing anything here--we just want to ignore the error
            }
        } while (hasItem);
    }
}
于 2021-08-09T14:04:37.833 回答
0

避免错误的一种方法是另辟蹊径。不要检查用户是否是组的成员,而是首先检索所有组并检查目标组的列表。缺点之一:速度较慢......

var groups = UserPrincipal.Current.GetAuthorizationGroups();
var found = groups.FirstOrDefault(principal => principal.Name == "Administrators");
var isMemberOfAdminGroup = found != null;

谢阿鲁斯的帮助:)

于 2014-05-14T13:58:15.820 回答
0
public static bool UserHasLocalAdminPrivledges(this UserPrincipal up)
{
   SecurityIdentifier id = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);
   return up.GetAuthorizationGroups().Any(g => g.Sid == id)
}
于 2019-06-12T06:35:14.213 回答
-1

有三种可能的解决方案(全部未经测试,对所有类型的域组使用最后一个自己):
1)加载组并自己枚举成员
2)加载组的底层对象并使用属性[“Members”],其中是一个 SID 列表。
3) 使用用户的 GetAuthorizationGroups() (这也将使用您的非直接组,服务帐户最终必须是“Windows 授权组”和“PreWindows 2000 兼容...”的成员)并使用组列表查找您的管理员组。

于 2012-10-24T14:53:45.657 回答