代码之家  ›  专栏  ›  技术社区  ›  tvanfosson

GroupPrincipal.Members.Remove()不适用于大型广告组

  •  5
  • tvanfosson  · 技术社区  · 16 年前

    我在用电话 System.DirectoryServices.AccountManagement 命名空间类来管理多个组的成员身份。这些集团控制着我们印刷会计系统的人口,其中一些数量非常庞大。我在从这些大型组中删除任何用户时遇到问题。我有一个测试程序来说明这个问题。请注意,我正在测试的组不是嵌套的,但是user.IsMemberOf()似乎也有相同的问题,而GetAuthorizationGroups()正确地显示了用户所属的组。讨论中的组大约有81K个成员,这超出了它应该拥有的数量,因为Remove()不起作用,通常大约有65K个成员。

    我很想听听其他有这个问题并解决了这个问题的人的意见。我和微软有一个公开的案例,但是电话的周转很慢,因为呼叫中心大约有17个小时的时差,所以他们要到我通常回家前一个小时才能到达工作地点。

    using (var context = new PrincipalContext( ContextType.Domain ))
    {
        using (var group = GroupPrincipal.FindByIdentity( context, groupName ))
        {
            using (var user = UserPrincipal.FindByIdentity( context, userName ))
            {
                if (user != null)
                {
                    var isMember = user.GetAuthorizationGroups()
                                       .Any( g => g.DistinguishedName == group.DistinguishedName );
                    Console.WriteLine( "1: check for membership returns: {0}", isMember );
                    if (group.Members.Remove( user ))
                    {
                        Console.WriteLine( "user removed successfully" );
                        group.Save();
                    }
                    else
                    {
                        // do save in case Remove() is lying to me
                        group.Save();
                        Console.WriteLine( "user remove failed" );
                        var isStillMember = user.GetAuthorizationGroups()
                                                .Any( g => g.DistinguishedName == group.DistinguishedName );
                        Console.WriteLine( "2: check for membership returns: {0}", isStillMember );
    
                    }
                }
            }
        }
    }
    
    3 回复  |  直到 16 年前
        1
  •  5
  •   Gabriel Luci    6 年前

    原来这是GroupPrincipal.Members.Remove()代码中的一个错误,在该代码中,对于成员数超过1500的组,删除失败。这已经在.NET4.0Beta2中得到了修复。我不知道他们是否有计划将该修复向后移植到2.0/3.x中。

    解决方法是获取底层DirectoryEntry,然后使用Invoke在IADsGroup对象上执行Remove命令。

     var entry = group.GetUnderlyingObject() as DirectoryEntry;
     var userEntry = user.GetUnderlyingObject() as DirectoryEntry;
     entry.Invoke( "Remove", new object[] { userEntry.Path } );
    
        2
  •  1
  •   WhoIsRich    11 年前

    这篇文章帮我指出了正确的方向,只是想添加一些附加信息。

    using (var groupEntry = new DirectoryEntry(groupLdapPath))
    {
        groupEntry.Invoke("remove", new object[] { memberLdapPath });
        groupEntry.Invoke("add",    new object[] { memberLdapPath });
    }
    

    另外请注意,对于标准的“member”属性,您使用的是用户或组DiscriminatedName,但invoke需要具有LDAP://前缀的路径,否则会引发模糊的InnerException:

    Exception from HRESULT: 0x80005000
    
        3
  •  0
  •   oldkrot    12 年前
    public bool RemoveUserFromGroup(string UserName, string GroupName)
    {
       bool lResult = false;
       if (String.IsNullOrEmpty(UserName) || String.IsNullOrEmpty(GroupName)) return lResult;
       try
       {
          using (DirectoryEntry dirEntry = GetDirectoryEntry())
          {
             using (DirectoryEntry dirUser = GetUser(UserName))
             {
                if (dirEntry == null || dirUser == null)
                {
                   return lResult;
                }
                using (DirectorySearcher deSearch = new DirectorySearcher())
                {
                   deSearch.SearchRoot = dirEntry;
                   deSearch.Filter = String.Format("(&(objectClass=group) (cn={0}))", GroupName);
                   deSearch.PageSize = 1000;
                   SearchResultCollection result = deSearch.FindAll();
                   bool isAlreadyRemoved = false;
                   String sDN = dirUser.Path.Replace("LDAP://", String.Empty);
                   if (result != null && result.Count > 0)
                   {
                      for (int i = 0; i < result.Count; i++)
                      {
                         using (DirectoryEntry dirGroup = result[i].GetDirectoryEntry())
                         {
                            String sGrDN = dirGroup.Path.Replace("LDAP://", String.Empty);
                            if (dirUser.Properties[Constants.Properties.PROP_MEMBER_OF].Contains(sGrDN))
                            {
                               dirGroup.Properties[Constants.Properties.PROP_MEMBER].Remove(sDN);
                               dirGroup.CommitChanges();
                               dirGroup.Close();
                               lResult = true;
                               isAlreadyRemoved = true;
                               break;
                            }
                         }
                         if (isAlreadyRemoved)
                            break;
                      }
                   }
                }
             }
          }
       }
       catch
       {
          lResult= false;
       }
       return lResult;
    }