البحث عن عضوية المجموعة المتكررة(Active Directory) باستخدام C#




.net active-directory (5)

    static List<SearchResult> ad_find_all_members(string a_sSearchRoot, string a_sGroupDN, string[] a_asPropsToLoad)
    {
        using (DirectoryEntry de = new DirectoryEntry(a_sSearchRoot))
            return ad_find_all_members(de, a_sGroupDN, a_asPropsToLoad);
    }

    static List<SearchResult> ad_find_all_members(DirectoryEntry a_SearchRoot, string a_sGroupDN, string[] a_asPropsToLoad)
    {
        string sDN = "distinguishedName";
        string sOC = "objectClass";
        string sOC_GROUP = "group";
        string[] asPropsToLoad = a_asPropsToLoad;
        Array.Sort<string>(asPropsToLoad);
        if (Array.BinarySearch<string>(asPropsToLoad, sDN) < 0)
        {
            Array.Resize<string>(ref asPropsToLoad, asPropsToLoad.Length+1);
            asPropsToLoad[asPropsToLoad.Length-1] = sDN;
        }
        if (Array.BinarySearch<string>(asPropsToLoad, sOC) < 0)
        {
            Array.Resize<string>(ref asPropsToLoad, asPropsToLoad.Length+1);
            asPropsToLoad[asPropsToLoad.Length-1] = sOC;
        }

        List<SearchResult> lsr = new List<SearchResult>();

        using (DirectorySearcher ds = new DirectorySearcher(a_SearchRoot))
        {
            ds.Filter = "(&(|(objectClass=group)(objectClass=user))(memberOf=" + a_sGroupDN + "))";
            ds.PropertiesToLoad.Clear();
            ds.PropertiesToLoad.AddRange(asPropsToLoad);
            ds.PageSize = 1000;
            ds.SizeLimit = 0;
            foreach (SearchResult sr in ds.FindAll())
                lsr.Add(sr);
        }

        for(int i=0;i<lsr.Count;i++)
            if (lsr[i].Properties.Contains(sOC) && lsr[i].Properties[sOC].Contains(sOC_GROUP))
                lsr.AddRange(ad_find_all_members(a_SearchRoot, (string)lsr[i].Properties[sDN][0], asPropsToLoad));

        return lsr;
    }

    static void Main(string[] args)
    {
    foreach (var sr in ad_find_all_members("LDAP://DC=your-domain,DC=com", "CN=your-group-name,OU=your-group-ou,DC=your-domain,DC=com", new string[] { "sAMAccountName" }))
        Console.WriteLine((string)sr.Properties["distinguishedName"][0] + " : " + (string)sr.Properties["sAMAccountName"][0]);
    }

إنني أتطلع إلى الحصول على قائمة بجميع المجموعات التي يكون المستخدم عضوًا بها في Active Directory ، وكلاهما مدرجين بشكل صريح في قائمة خصائص memberOf بالإضافة إلى ضمنيًا من خلال عضوية مجموعة متداخلة. على سبيل المثال ، إذا قمت بفحص UserA و UserA هو جزء من GroupA و GroupB ، فأنا أريد أيضًا إدراج GroupC إذا كانت GroupB عضوًا في GroupC.

لنقدم لك مزيدًا من المعلومات عن طلبي ، سأفعل ذلك على أساس محدود. في الأساس ، أرغب في إجراء فحص أمني من وقت لآخر يسرد هذه العضويات الإضافية. سأرغب في التفريق بين الاثنين ولكن هذا لا ينبغي أن يكون صعبا.

مشكلتي هي أنني لم أجد طريقة فعالة لجعل هذا الاستعلام يعمل. يُظهر النص القياسي على Active Directory ( مقالة CodeProject ) طريقة للقيام بذلك بشكل أساسي عبارة عن بحث تكراري. هذا يبدو غير فعال بشكل رهيب. حتى في نطاقي الصغير ، قد يحصل المستخدم على أكثر من 30 عضوًا في المجموعة. وهذا يعني 30+ مكالمات إلى Active Directory لمستخدم واحد.

لقد بحثت في شفرة LDAP التالية للحصول على جميع إدخالات العضو في وقت واحد:

(memberOf:1.2.840.113556.1.4.1941:={0})

حيث سيكون {0} مسار LDAP (مثل: CN = UserA ، OU = Users ، DC = foo ، DC = org). ومع ذلك ، فإنه لا يقوم بإرجاع أية سجلات. الجانب السلبي لهذه الطريقة ، حتى لو نجحت ، هو أنني لن أعرف أي مجموعة كانت صريحة وأيها ضمني.

هذا ما لدي حتى الآن. أود أن أعرف ما إذا كانت هناك طريقة أفضل من مقالة CodeProject ، وإذا كان الأمر كذلك ، فكيف يمكن تحقيق ذلك (الشفرة الفعلية ستكون رائعة). أنا أستخدم .NET 4.0 و C #. My Active Directory هو على مستوى وظيفي في Windows 2008 (وهو ليس R2 حتى الآن).


يمكنك الاستفادة من خصائص tokenGroups و tokenGroupsGlobalAndUniversal إذا كنت على خادم Exchange. ستقدم لك tokenGroups جميع مجموعات الأمان التي ينتمي إليها هذا المستخدم ، بما في ذلك المجموعات المتداخلة ومستخدمي النطاق والمستخدمين وما إلى ذلك. tokenGroupsGlobalAndUniversal ستشمل كل شيء من tokenGroups ومجموعات التوزيع

private void DoWorkWithUserGroups(string domain, string user)
    {
        var groupType = "tokenGroupsGlobalAndUniversal"; // use tokenGroups for only security groups

        using (var userContext = new PrincipalContext(ContextType.Domain, domain))
        {
            using (var identity = UserPrincipal.FindByIdentity(userContext, IdentityType.SamAccountName, user))
            {
                if (identity == null)
                    return;

                var userEntry = identity.GetUnderlyingObject() as DirectoryEntry;
                userEntry.RefreshCache(new[] { groupType });
                var sids = from byte[] sid in userEntry.Properties[groupType]
                           select new SecurityIdentifier(sid, 0);

                foreach (var sid in sids)
                {
                    using(var groupIdentity = GroupPrincipal.FindByIdentity(userContext, IdentityType.Sid, sid.ToString()))
                    {
                        if(groupIdentity == null)
                            continue; // this group is not in the domain, probably from sidhistory

                        // extract the info you want from the group
                    }
                }
            }
        }
    }

إذا لم تكن هناك طريقة أخرى غير المكالمات العودية (ولا أعتقد أن هناك) ، فعلى الأقل يمكنك أن تجعل الإطار يقوم بالعمل نيابة عنك: راجع أسلوب UserPrincipal.GetAuthorizationGroups (في مساحة الاسم System.DirectoryServices.AccountManagement وعرضه في. NET 3.5)

تبحث هذه الطريقة في جميع المجموعات بشكل متكرر وتعيد المجموعات التي يكون المستخدم عضوًا فيها. قد تتضمن المجموعة التي تم إرجاعها أيضًا مجموعات إضافية يعتبر النظام المستخدم فيها عضوًا لأغراض التخويل.

مقارنة مع نتائج GetGroups ("إرجاع مجموعة من كائنات المجموعة التي تحدد المجموعات التي يكون فيها المدير الحالي عضوًا") لمعرفة ما إذا كانت العضوية صريحة أو ضمنية.


استخدم عامل التصفية ldap بشكل متكرر ولكن الاستعلام عن كل المجموعات التي يتم إرجاعها بعد كل استعلام لتقليل عدد الرحلات المستديرة.

مثلا:

  1. احصل على جميع المجموعات التي يكون فيها المستخدم عضوًا
  2. احصل على جميع المجموعات التي تكون مجموعات الخطوة 1 أعضاء فيها
  3. احصل على جميع المجموعات التي تكون فيها مجموعات الخطوة 2 أعضاءً
  4. ...

في تجربتي هناك نادرا ما يكون أكثر من 5 ولكن يجب أن يكون أقل بكثير من ذلك بكثير 30.

أيضا:

  • تأكد من سحب الخصائص التي ستحتاجها مرة أخرى.
  • يمكن أن تساعد نتائج التخزين المؤقت في الأداء بشكل كبير ، ولكنها جعلت كوداتي أكثر تعقيدًا.
  • تأكد من استخدام تجمع الاتصالات.
  • يجب التعامل مع المجموعة الأساسية بشكل منفصل

عطش شكرا لهذا سؤال مثير للاهتمام.

بعد ذلك ، مجرد تصحيح ، أنت تقول:

لقد بحثت في شفرة LDAP التالية للحصول على جميع إدخالات العضو في وقت واحد:

(memberOf:1.2.840.113556.1.4.1941:={0})

أنت لا تجعلها تعمل. أتذكر أنني جعله يعمل عندما علمت عن وجودها ، ولكن كان في فلتر LDIFDE.EXE. لذلك أطبقه على ADSI في C # ولا يزال يعمل. كان هناك الكثير من قوسين في العينة التي أخذتها من Microsoft ، ولكنها كانت تعمل ( المصدر في AD Filter Filter Syntax ).

وفقًا لملاحظتك بخصوص حقيقة أننا لا نعرف ما إذا كان المستخدم ينتمي بشكل صريح إلى المجموعة ، أقوم بإضافة طلب آخر. أعرف أن هذا ليس جيدًا جدًا ، لكنه أفضل ما يمكنني فعله.

static void Main(string[] args)
{
  /* Connection to Active Directory
   */
  DirectoryEntry deBase = new DirectoryEntry("LDAP://WM2008R2ENT:389/dc=dom,dc=fr");


  /* To find all the groups that "user1" is a member of :
   * Set the base to the groups container DN; for example root DN (dc=dom,dc=fr) 
   * Set the scope to subtree
   * Use the following filter :
   * (member:1.2.840.113556.1.4.1941:=cn=user1,cn=users,DC=x)
   */
  DirectorySearcher dsLookFor = new DirectorySearcher(deBase);
  dsLookFor.Filter = "(member:1.2.840.113556.1.4.1941:=CN=user1 Users,OU=MonOu,DC=dom,DC=fr)";
  dsLookFor.SearchScope = SearchScope.Subtree;
  dsLookFor.PropertiesToLoad.Add("cn");

  SearchResultCollection srcGroups = dsLookFor.FindAll();

  /* Just to know if user is explicitly in group
   */
  foreach (SearchResult srcGroup in srcGroups)
  {
    Console.WriteLine("{0}", srcGroup.Path);

    foreach (string property in srcGroup.Properties.PropertyNames)
    {
      Console.WriteLine("\t{0} : {1} ", property, srcGroup.Properties[property][0]);
    }

    DirectoryEntry aGroup = new DirectoryEntry(srcGroup.Path);
    DirectorySearcher dsLookForAMermber = new DirectorySearcher(aGroup);
    dsLookForAMermber.Filter = "(member=CN=user1 Users,OU=MonOu,DC=dom,DC=fr)";
    dsLookForAMermber.SearchScope = SearchScope.Base;
    dsLookForAMermber.PropertiesToLoad.Add("cn");

    SearchResultCollection memberInGroup = dsLookForAMermber.FindAll();
    Console.WriteLine("Find the user {0}", memberInGroup.Count);

  }

  Console.ReadLine();
}

في شجرة الاختبار الخاصة بي ، هذا يعطي:

LDAP://WM2008R2ENT:389/CN=MonGrpSec,OU=MonOu,DC=dom,DC=fr
adspath : LDAP://WM2008R2ENT:389/CN=MonGrpSec,OU=MonOu,DC=dom,DC=fr
cn : MonGrpSec
Find the user 1

LDAP://WM2008R2ENT:389/CN=MonGrpDis,OU=ForUser1,DC=dom,DC=fr
adspath : LDAP://WM2008R2ENT:389/CN=MonGrpDis,OU=ForUser1,DC=dom,DC=fr
cn : MonGrpDis
Find the user 1

LDAP://WM2008R2ENT:389/CN=MonGrpPlusSec,OU=ForUser1,DC=dom,DC=fr
adspath : LDAP://WM2008R2ENT:389/CN=MonGrpPlusSec,OU=ForUser1,DC=dom,DC=fr
cn : MonGrpPlusSec
Find the user 0

LDAP://WM2008R2ENT:389/CN=MonGrpPlusSecUniv,OU=ForUser1,DC=dom,DC=fr
adspath : LDAP://WM2008R2ENT:389/CN=MonGrpPlusSecUniv,OU=ForUser1,DC=dom,DC=fr
cn : MonGrpPlusSecUniv
Find the user 0

(تحرير) '1.2.840.113556.1.4.1941' لا يعمل في W2K3 SP1 ، فإنه يبدأ في العمل مع SP2. أفترض أنه هو نفسه مع W2K3 R2. من المفترض أن تعمل على W2K8. أنا اختبار هنا مع W2K8R2. سوف أكون قريبا قادرة على اختبار هذا على W2K8.







active-directory