إدارة عضوية المجموعة عبر المصادقة

@david متابعة النقاش من

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

لنستكمل النقطة التي أشرت إليها.

كيف تقرر أي المجموعات تُضاف منها أو تُحذف منها المستخدم؟ وكيف يؤثر ذلك على إضافة/حذف المستخدمين يدويًا من المجموعات في Discourse نفسها؟

  1. يقوم المستخدم بالمصادقة عبر OIDC مع المجموعات ['group1', 'group2']
  2. في واجهة مستخدم Discourse، يُضاف المستخدم إلى group3
  3. لاحقًا، يقوم نفس المستخدم بالمصادقة عبر OIDC مع المجموعات ['group1']

عند فحص هذا كإنسان، يمكننا أن نرى أن الحالة النهائية يجب أن تكون group1, group3 (يجب إزالة group2). لكنني لا أعتقد أن هناك ما يكفي من الحالة (state) يتم تتبعها لاتخاذ هذا القرار برمجياً. وبالمثل:

  1. يقوم المستخدم بالمصادقة عبر OIDC مع المجموعات ['group1', 'group2']
  2. في واجهة مستخدم Discourse، يُحذف المستخدم من group1
  3. لاحقًا، يقوم نفس المستخدم بالمصادقة عبر OIDC مع المجموعات ['group1', 'group2']

ماذا يجب أن يحدث الآن؟ قام المسؤول بحذف المستخدم صراحةً من group1، لكن OIDC أعاده إليها للتو. هذا تجربة مستخدم (UX) مربكة جدًا للمسؤول. قد نحتاج إلى طريقة ما لتحديد المجموعات على أنها “مدارة خارجيًا”، وإخفاء جميع واجهات الإضافة/الحذف :thinking:

أعتقد أن هناك عدة طرق يمكننا من خلالها معالجة هذه المشكلة (وهي غير حصرية):

تحديد المجموعة وسمات الرمز المميز (token)

في أي تطبيق، يجب أن يكون التحديد الصريح لـ:

  1. أي المجموعات يمكن إدارة عضويتها عبر المصادقة؛ و
  2. أي السمات في رمز المصادقة (auth token) تحكم عضوية المجموعة

مطلوبًا، سواء كان ذلك عبر إعدادات الموقع أو إعدادات المجموعة أو غير ذلك، ويجب أن يكون الافتراضي “معطلًا”.

المعالجة الصارمة أو المرنة

تُعتبر المعالجة “صارمة” إذا تم حذف المستخدم عند غياب المجموعة عن سمة الرمز المميز المحددة. وتُعتبر المعالجة “مرنة” إذا لم يتم حذف المستخدم عند غياب المجموعة عن سمة الرمز المميز المحددة.

يتم تعيين حالة الصارمة/المرنة على أساس كل مجموعة على حدة. هناك مثال لكيفية القيام بذلك في إعداد موقع هنا: Handle groups by mattcg · Pull Request #7 · discourse/discourse-openid-connect · GitHub. يمكن اتباع نفس النهج عبر إعداد المجموعة.

كما اقترحت، يمكنك تعطيل إضافة/حذف عضوية المجموعة عبر إدارة المجموعات إذا كانت المعالجة “صارمة”.

تحديد مصدر العضوية

يمكنك أيضًا تخزين مصدر عضوية المجموعة في جدول group_users للسماح بنهج “مختلط” داخل المجموعة، أي لا يمكن للمسؤول حذف العضويات التي تم إنشاؤها عبر رمز المصادقة.

كلما فكرت في هذا أكثر، زادت قنارتي بأن هذا يجب أن يتم عبر إعدادات المجموعة.

11 إعجابًا

شكرًا لك @angus على بدء هذا الموضوع! أعرف أن الكثير من الناس متحمسون لهذه الوظيفة!

مثير للاهتمام! كنت أتخيل دائمًا أن المجموعات تُنشأ تلقائيًا أثناء المصادقة، وأن اسم المجموعة يتطابق 1:1 مع اسم المجموعة لدى مزود الهوية. هذا هو الحال حاليًا مع DiscourseConnect.

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

هذا يبدو جيدًا من الناحية التقنية. قلقي الوحيد هو أن شرحه للمستخدمين/المشرفين قد يكون صعبًا. إذا اتبعنا فكرتك حول “التحديد الصريح” للمجموعات التي تديرها المصادقة، أعتقد أننا يمكننا ببساطة تنفيذ المعالجة “الصارمة”؟

عندما يتم تكوين مجموعة لتكون خاضعة لإدارة المصادقة، يتم إخفاء الإضافة/الإزالة اليدوية خلف تحذير كبير، وتتصرف كما في وضع “الصارم” الذي وصفته.

كيف يبدو ذلك؟

ملاحظة جانبية: يجب علينا أيضًا التأكد من تسجيل جميع عمليات الإضافة/الإزالة التلقائية للمستخدمين في سجل المجموعة.这将使每个人更容易理解发生了什么以及为什么。

7 إعجابات

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

ربما يمكننا الانتقال إلى الإنشاء التلقائي كخيار بعد تنفيذ النسخة الصريحة؟

من حيث إعدادات المجموعة، سيكون الأمر على النحو التالي:

قسم الإعدادات: العضوية (أي القسم الحالي)
عنوان مجموعة الإعدادات: إدارة المصادقة
الإعدادات:

  • الخدمة: قائمة بخدمات المصادقة. ستكون “الكل” خيارًا. سيعمل هذا الإعداد أيضًا كحالة “مفعل/معطل” لمجموعة الميزات هذه. أي أن القيمة الافتراضية ستكون “لا شيء”.
  • الادعاء: حقل إدخال نصي لتحديد ادعاء رمز الهوية. ستشرح “الوصف” لهذه الميزة (ربما في منشور هنا على ميتا) التنسيقات المدعومة، مثل: القيم المنطقية، السلسلة المفصولة بفواصل، إلخ.
  • الوضع: انظر أدناه

نعم، إذا كان هناك إعداد “صارم/مرن”، فسيكون علينا شرحه بإيجاز. أعتقد أن الطريقة المثلى للتعامل مع ذلك هي التركيز على “الإضافة” و"الإزالة". يمكنك ربما وصف الأمر على النحو التالي:

الإعداد: “الوضع”

الخيار 1 (مرن):

  • التسمية: “إضافة الأعضاء”
  • الوصف: “السماح بإضافة الأعضاء إلى هذه المجموعة أثناء المصادقة”

الخيار 2 (صارم):

  • التسمية: “إضافة وإزالة الأعضاء”
  • الوصف: “السماح بإضافة الأعضاء وإزالتهم أثناء المصادقة. سيؤدي هذا إلى تعطيل أدوات التحكم اليدوية في العضوية.”

السبب في أنني حريص على الاحتفاظ بالخيار “المرن” هو أنه عند العمل مع العملاء على هذا النوع من الأمور سابقًا، كان هذا هو حالة الاستخدام الأكثر شيوعًا، أي:

  • الحاجة الرئيسية هي السماح بالوصول إلى مجموعة معينة بناءً على حالة في خدمة خارجية.

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

  • غالبًا ما يكون هناك رغبة في الاحتفاظ بالقدرة على التحكم يدويًا في العضوية داخل Discourse. عندما قمت بتطبيق النهج “الصارم”، كان ذلك “مفاجئًا” (أي “لماذا فقد الشخص X عضويته؟”) على الرغم من الشروحات وكونه يعمل (تقنيًا) كما ينبغي.

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

ربما نضيف نوعًا جديدًا من إجراءات “إزالة المستخدم” و"إضافة المستخدم" تتضمن خدمة المصادقة المسؤولة عن الإجراء، أي “إزالة المستخدم ([اسم الخدمة])”.

3 إعجابات

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

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

5 إعجابات

@david ما رأيكم في هذا؟ إذا قمت بإعداد طلب سحب (PR)، هل ستكونون منفتحين عليه؟

إعجابَين (2)

هذا عمل قيم تقومون به هنا! :sunflower:

فقط كمقارنة وظيفية، أردت مشاركة ما نقوم به في الشبكة العالمية لتمكين القانوني، والتي تستخدم ووردبريس وإضافة ووردبريس الخاصة بـ Discourse. قمنا بضبط النظام بحيث يتم تحديث كل مستخدم في ووردبريس، فيتم تحديثه تلقائيًا في Discourse. ويشمل ذلك بعض المجموعات الخاصة (مثل: العضو الأساسي، المساهم في الموارد، إلخ) بالإضافة إلى تفاصيل الملف الشخصي. أضفنا حقل مستخدم مخفي لـ “آخر تحديث” ساعد في استكشاف الأخطاء والتأكد من عمل النظام بشكل صحيح.

نقوم بقفل هذه المجموعات التي تُدار عن بُعد بحيث لا يمكن للمستخدمين الانضمام إليها أو مغادرتها داخل Discourse، لكننا لم نرَ ضرورة لمنع إدارة العضوية في المجموعات من قِبل الموظفين. يعجبني ما تحاولونه أعلاه، لكنه يتجاوز قدراتي الشخصية بصراحة.

لدينا بعض عملاء Discourse for Teams الذين يستخدمون Okta بنشاط لإدارة الوصول إلى جميع تطبيقات شركتهم. يقومون أيضًا بإعداد أدوار هناك يتم دعمها لاحقًا، مثل توفير مستويات معينة من الوصول إلى Microsoft Tableau وما شابه. كما أن Okta يعمل كدليل، حيث يديرون صور المستخدمين، والنبذة التعريفية، والموقع، وغيرها من معلومات الملف الشخصي. ويودون رؤية تحديث هذه المعلومات في Teams خارجيًا أيضًا من خلال Okta.

5 إعجابات

يجب أن نحاول إبقاء الأمور فائقة التقنية خارج إعدادات المجموعة قدر الإمكان. إذا استدعى الأمر ربطًا بوثائق بناء الجملة (Syntax)، فهذا يعني غالبًا أنه يجب تبسيطها أو نقلها إلى إعدادات موقع المسؤول. أعتقد أننا يجب أن نترك هذا الجزء لكل مكون مصادقة (Auth plugin) لتنفيذه، لأن التطبيق قد يختلف اختلافًا كبيرًا.

باعتبار OIDC مثالاً، سيضيف هذا المكون إعداد موقع جديد باسم openid_connect_roles_claim. إذا كنت تستخدم Okta، فسيقوم المسؤول بتعيينه كـ groups. أعتقد أن مصفوفة السلاسل النصية (string array) هي تنسيق قياسي إلى حد ما لـ OIDC، ولكن يمكننا استكشاف خيارات أكثر تعقيدًا هنا إذا لزم الأمر حقًا.

لتلقي هذه المعلومات، سيتم منح Auth::Result سمة جديدة تسمى roles، والتي تقبل مصفوفة بسيطة من السلاسل النصية. سيقوم النواة (Core) بعد ذلك بأخذ هذا (في Auth::Result#apply_user_attributes!)، وتحميله في جدول جديد يسمى UserAssociatedRoles يحتوي على الأعمدة (provider_name، user_id، role). يوفر جدول UserAssociatedRoles هذا “تحديد مصدر العضوية” الذي ذكرته في المنشور الأصلي (OP).

أتخيل أن إعدادات المجموعة ستبدو شيئًا كهذا:

سيتم إضافة بادئة provider_name لأسماء الأدوار. سيُبنى الإكمال التلقائي على جميع القيم الموجودة حاليًا في جدول UserAssociatedRoles، لكننا سنقبل أيضًا القيم غير المكتملة تلقائيًا في حال لم يكن الدور معروفًا لدى Discourse بعد.

جمالية وجود جدول كامل user_associated_roles في قاعدة البيانات تكمن في أن المسؤولين يمكنهم فعل ما يريدونه مع مجموعات Discourse، وستتم تحديث العضويات فورًا دون الحاجة إلى تسجيل دخول المستخدمين مرة أخرى.

هذا منطقي. أعتقد أن الأفضل هو الحفاظ على البساطة قدر الإمكان، على الأقل في الإصدار v1، لذا أفضل عدم وجود “أوضاع” متعددة. ماذا لو جعلنا السلوك الافتراضي كالتالي:

  • إضافة الأعضاء عندما يكون لديهم دور مطابق من مورد المصادقة
  • إزالة الأعضاء عندما يختفي هذا الدور
  • السماح بإضافة/إزالة الأعضاء يدويًا في Discourse أيضًا
  • إذا حاول مسؤول إزالة مستخدم تم إضافته عبر مورد مصادقة، فسيظهر تحذير: “قد يتم إضافة هذا المستخدم مرة أخرى في المرة القادمة التي يسجل فيها دخول”

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

بالنسبة للمواقع التي لا تريد ذلك حقًا، يمكننا وجود إعداد موقع مثل remove_group_membership_when_auth_role_lost (الافتراضي: true).

نعم بالتأكيد. لدينا هذه المشكلة أيضًا مع بيانات المستخدم الوصفية الأخرى مثل الاسم، والصورة الرمزية، إلخ. أعتقد أنه في وقت قريب جدًا، نحتاج إلى النظر في إنشاء ما يعادل sync_sso لمكونات المصادقة الأخرى. يمكن تمرير جميع المعلومات التي يتم تمريرها عادةً عبر OIDC / SAML / إلخ، بما في ذلك معلومات “الدور” الجديدة هذه. ربما يكون مشروعًا منفصلاً على الرغم من ذلك.


كيف يبدو كل ذلك بالنسبة لك يا @angus؟ إنه أقل مرونة قليلاً من وجود أوضاع صارمة/متساهلة منفصلة، لكنني أعتقد أن وجود وضع واحد فقط سيجعل استكشاف الأخطاء وإصلاحها / التوثيق / الدعم أسهل بكثير.


مع هذا الخطة، إليك نظرة عامة عالية المستوى على كيفية ظهور الأمر من منظور المسؤول:

الإعداد الأولي

  1. قم بإعداد مصادقة Okta، وقم بتفعيل مطالبة المجموعات (groups claim) من جانب Okta

  2. في Discourse، قم بتعيين openid_connect_roles_claim إلى groups

إعداد مجموعة جديدة

  1. قم بإنشاء مجموعة في Discourse كالمعتاد. قم بتكوين الاسم / الاسم الكامل / الزخرفة (flair) / إلخ. بما تريد

  2. انتقل إلى تفضيلات “العضوية”، ضع المؤشر في قائمة منسدلة لأدوار SSO، واختر دورًا من القائمة. إذا لم يكن Discourse قد رأى شخصًا سجل دخوله بالدور بعد، فستحتاج إلى إدخاله يدويًا

    يمكنك تحديد أدوار متعددة لمجموعة واحدة في Discourse. على سبيل المثال، قد تكون مجموعة “فريق” في Discourse مزيجًا من oidc:employees و oidc:contractors

  3. اضغط على حفظ، وسيتم تحديث عضوية المجموعة فورًا بمعلومات الدور التي قام Discourse بتخزينها مؤقتًا من تسجيلات الدخول السابقة.

  4. في تسجيلات الدخول المستقبلية، سيتم عكس أي تغييرات في أدوار مورد الهوية في مجموعة Discourse

  5. لا يزال بإمكانك إضافة/إزالة المستخدمين إلى المجموعة في واجهة المستخدم الأصلية لـ Discourse

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

إعجابَين (2)

أتفق معك.

نعم، ومع ذلك توجد حالات يكون فيها دعم ادعاء (claim) من نوع boolean منطقيًا. لكن نعم، أوافق على أن نبسط الأمور حاليًا من خلال دعم مصفوفة السلاسل النصية فقط.

بالنسبة للتنفيذ المخصص لكل أداة مصادقة على حدة، أعتقد أننا سنحتاج أيضًا إلى طريقة group_sync_enabled? داخل Auth::Authenticator، والتي سيتم تجاوزها (override) في أدوات المصادقة الفردية، وفي أدوات المصادقة العامة بناءً على وجود قيمة في الإعداد ذي الصلة.

هذا يثير في الواقع سؤالًا مثيرًا للاهتمام. يمكن استخدام هذه الآلية أيضًا من قِبل أدوات المصادقة الخاصة بالخدمة المحددة، مثل فيسبوك (“groups”؟)، وجوجل (“groups”؟)، وديسكورد (“roles”؟). لست متأكدًا مما إذا كانت رموز الهوية (id tokens) الخاصة بهم تحتوي على مثل هذه المعلومات، لكن ربما يستحق الأمر النظر فيه من منظور التصميم التقني (أي إمكانية إضافة هذا لاحقًا). لا أعتقد أن هذا مصدر قلق رئيسي للإصدار الأول (v1).

تبدو هذه الآليات مقبولة، لكنني أتساءل لماذا انتقلنا من استخدام “المجموعات” إلى استخدام “الأدوار” (roles) هنا. ليست مشكلة كبيرة، لكنني أريد التأكد من عدم وجود شيء فاتني.

من منظور تجربة المستخدم (UX)، أرى أن استخدام هذا المصطلح قد يسبب ارتباكًا (أي استخدام “الأدوار” و"المجموعات" في آن واحد)، حتى لو كان ذلك مبنيًا على تمييز تقني مفيد. كما أنه من الممكن أن يسبب ارتباكًا للمطورين إذا لم يكونوا على دراية بهذا السياق الخلفي.

أعجبني فكرة الجدول المنفصل، ومع ذلك ربما ينبغي أن يكون لدينا ارتباط بمفتاح أجنبي (foreign key) مع user_associated_accounts بدلاً من provider_name؟ قد يكون ذلك مفيدًا في عمليات مثل عمليات التنظيف. أعتقد أن الإجابة على هذا السؤال تعتمد جزئيًا على مدى رغبتنا في ربط ارتباط المجموعة/الدور بحساب المستخدم المرتبط من منظور المنتج. هل هناك أي عواقب سلبية لربط الاثنين الآن؟

** تعديل: أعتقد أننا نملك بالفعل هذا الارتباط عبر user_id.

يبدو ذلك مقبولاً، لكنني أفكر فقط في أن لدينا هذه المعلومات أيضًا على أساس المزود بفضل إعداد الموقع الذي اقترحته، والذي سيغطي أيضًا الحالة التي لم يُرَ فيها الدور بعد. ربما هناك طريقة لاستخدام Discourse.authenticators هنا، أي قائمة في الذاكرة للمزودين وادعاءاتهم (claims)؟

أعتقد أن هذا حل وسط جيد. خاصة إذا كان لدينا أيضًا إعداد الموقع المذكور أدناه.

على أساس المكون الإضافي (أي أداة المصادقة)؟ إذا كان الأمر كذلك، فأنا موافق. يجب أن يغطي ذلك الحاجة للإصدار الأول (v1).

ملخص ممتاز لتدفق المستخدم :+1: في انتظار الاقتراحات البسيطة نسبيًا التي قدمتها، أنا موافق على هذا الاتجاه.

إعجاب واحد (1)

:+1:

نعم بالتأكيد. كنت أفكر بشكل خاص في Google، لأن الناس يحبون استخدامها لمنظمات GSuite الخاصة بهم، والتي أفترض أنها تمتلك مفهوم المجموعات.

أعتقد أنني كنت أحاول تجنب أي لبس بين “مجموعات مزود الهوية” و"مجموعات Discourse"… لكن من الممكن أنني جعلت الأمور أكثر إرباكًا بتغيير الاسم. أنا سعيد جدًا بالالتزام بكلمة “المجموعات” هنا.

كان تفكيري هو أننا ما زلنا ندعم مصادقات مزودي المصادقة غير المدارة، لذا قد لا توجد سجل user_associated_account. لا يزال بإمكاننا الربط معه عبر user_id كما قلت :+1:

لست متأكدًا من أن إعداد الموقع سيساعد هنا. إذا كنا نتحدث عن مصفوفة من السلاسل النصية تمثل “أدوارًا”، فإن إعداد الموقع لن يحدد ما هي الأدوار فعليًا. بل سيحدد فقط كيفية الحصول على المصفوفة. هل هذا منطقي؟

يبدو رائعًا :smiley:

إعجاب واحد (1)

نعم، أنت محق. كنت لا أزال أفكر في تنفيذ سابق قمت فيه بتضمين مجموعات محددة في الإعداد نفسه، لكننا لا نفعل ذلك هنا، وهذا لا بأس به.

أعتقد أن هناك معلومات كافية هنا للبدء في العمل على طلب السحب (PR)، وسأبدأ في ذلك في وقت لاحق من هذا الأسبوع، ما لم يكن لديك أي مخاوف متبقية؟ سأقوم بتحديث هنا إذا ظهرت أي مسائل في الطريق.

يبدو رائعًا - يسعدني مساعدتك في أي مشاكل أو أسئلة قد تطرأ على طول الطريق :slight_smile:

إعجابَين (2)

حسناً، لقد وصلت أخيراً إلى مرحلة يمكنني فيها مشاركة العمل قيد التطوير هنا

بعض الملاحظات.

لقد اخترت حالة Google Apps HD Groups الأكثر تعقيداً قليلاً للتنفيذ الأولي، لأنني أعتقد أن ذلك يساعد في استيعاب التباديل المحتملة لهذه الحالة، مثل الحاجة إلى أخذ المجموعات الخاصة بالنطاق من مزود الخدمة في الاعتبار.

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

لاحظ أنه لتطبيق حالة مجموعات Google HD، يجب أن تمنح الأعضاء غير المسؤولين في مجموعات Google Apps HD الخاصة بك صلاحية المسؤول المفوض من أجل سرد مجموعاتهم (عبر واجهة برمجة تطبيقات دليل الإدارة). يوجد في الواقع دور إداري مُعد مسبقاً يُسمى “Groups Reader” في النسخة التجريبية (beta) يعمل بشكل جيد لهذا الغرض. راجع Prebuilt administrator roles  |  User management  |  Google Workspace Help

يعمل تطبيق Google بنجاح. إذا قمت بإعداده ثم قمت بالمصادقة مع HD، فستكون مجموعات Google HD الخاصة بك متاحة في إعداد عضوية المجموعة التلقائية، وسيتم إضافتك إلى مجموعة Discourse هذه إذا تم اختيار مجموعة HD هذه، وسيتم إزالتك إذا تم إزالتها (مع تسجيل كلتا الإجراءين بدقة)، وسيتم إضافة المستخدمين اللاحقين في مجموعة Google HD هذه الذين يقومون بالمصادقة فوراً.

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


بعض المهام التقنية المتبقية (إلى جانب الأسئلة المفاهيمية/المنتجة المذكورة أعلاه). الترحيب أيضاً بالاقتراحات في هذا الجانب:

  • ربما يتم تسلسل label الخاص بـ associated_groups (بدلاً من نمذجته على جانب العميل).
  • إضافة الاختبارات المفقودة ووحدة QUnit.
  • ربما نقل إنشاء/تدمير user_associated_group أو group_associated_group إلى مهمة في الخلفية، حيث يمكن أن يكون ذلك بطيئاً مع الأعداد الكبيرة.
3 إعجابات

هذا يبدو رائعًا جدًا!

أنا متردد قليلاً بشأن مسألة “التفويض الثانوي”، وكذلك عمود provider_domain. هل يمكنك التوضيح أكثر لسبب حاجتنا إليهما؟ يبدو أنهما خاصان جدًا بـ Google… هل هناك سبب يمنعنا من طلب نطاق admin.directory.group.readonly أثناء طلب المصادقة الأولي؟ وربما نكتفي بإضافة بادئة اسم المجموعة مع النطاق؟ (أو استبعاد النطاق بالكامل، حيث أفترض أن الناس سيستخدمون هذا مع نطاق Google “المستضاف” واحد فقط؟)

نعم، أنا مرتاح تمامًا لوجود 3 جداول هنا - فهذا يحافظ على الأمور أكثر تنظيماً.

أتفق :+1:

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

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

إذا اقتربنا من 30 ثانية (مهلة طلب unicorn)، فقد نحتاج إلى التفكير في وظيفة خلفية.

قد نحتاج أيضًا إلى التفكير في إضافة قفل DistributedMutex هنا. على سبيل المثال، ماذا يحدث إذا سجل المستخدم الدخول أثناء معالجة تغيير في group_associated_group؟ أنا مستعد لمناقشة هذا النوع من الأمور على GitHub بمجرد أن ننهى من تحديد البنية العامة النهائية.

أتابع هذا باهتمام — وأضيف هذا الرابط ذو الصلة: Does `sso overrides groups` work with Oauth2?

في حالتي الخاصة، سأكون سعيدًا تمامًا بتطبيق سلوك Discourse Connect على مزوِّدي المصادقة الآخرين.

إعجابَين (2)

عظيم :slight_smile: نحن على الطريق الصحيح.

سأتناول كل نقطة على حدة.

التفويض التدريجي

السبب في عدم إمكانية طلب أذونات المجموعات في الطلب الأول هو أنك لا تعرف من يسجل الدخول، أو ما الذي يشعر بالراحة لمشاركته. يمكنك تقييد تعيين مجموعات Google للمستخدمين الذين يستخدمون إعداد google_oauth2_hd، ومع ذلك فإن هذا سيحد من نطاق الميزة إلى حد كبير. وجود فريق يستخدم تطبيقات Google بالإضافة إلى مستخدمين «عامة» يريدون أيضًا استخدام مصادقة Google أمر شائع نسبيًا.

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

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

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

من الصحيح أن Google دافعت عن التفويض التدريجي في مجال OAuth2، على سبيل المثال، جميع أوراق العمل حول هذا الموضوع كُتبها موظف في Google.

ومع ذلك، فإن المفهوم ليس خاصًا بـ Google (لا يسميه الآخرون بالضرورة «تفويضًا تدريجيًا»). إنه شائع نسبيًا بأشكال مختلفة في تطبيقات الهاتف المحمول، ويتم اعتماده في OAuth2 من قبل مزودين آخرين. على سبيل المثال، إليك وثائق Facebook حول نفس الموضوع.

من المرجح أنك على دراية بهذا المجال بالفعل، لكنه يُعتبر «ممارسة جيدة» أن:

  • تخبر الناس بأنك على وشك طلب أذونات أكثر من المعلومات الأساسية القياسية؛
  • وربما تشرح السبب.

إذا ضغط المستخدم على “تسجيل الدخول باستخدام Facebook” في نموذج تسجيل الدخول في Discourse، ثم طُلب منه، بالإضافة إلى بريده الإلكتروني، أيضًا الوصول إلى مجموعات فيسبوك الخاصة به، فقد ينسحب. تضع Facebook الأمر بهذه الطريقة:

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

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

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

كل مزود يحتاج فقط إلى:

  • إرجاع نتيجة تحتوي على secondary_authorization_url

  • استخدام معامل state للكشف عن أي طلب تفويض وصل إليه المستخدم

  • توفير omniauth_secondary_authorization_description لملف users/omniauth_callbacks/secondary_authorization.html.erb. على سبيل المثال، هذا هو النص لـ Google، الذي يراه المستخدم قبل تأكيد إعادة التوجيه للتفويض الثانوي:

    بما أنك سجلت الدخول باستخدام بريد إلكتروني من %{domain}، فنحن بحاجة إلى طلب إذن لعرض مجموعات %{domain} الخاصة بك.

لا يوجد أي من هذه الأجزاء مخصص لـ Google.

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

أخيرًا، تجدر الإشارة أيضًا إلى أن التفويض الثانوي ليس مطلوبًا لكي تعمل associated_groups. يمكن لمزود المصادقة ببساطة طلب نطاق المجموعات مقدمًا، ثم إضافة المجموعات إلى نتيجة المصادقة بعد استلام الاستجابة الأولى. في الواقع، يجب أن نبني ذلك كخيار في الإضافات الأساسية لـ oauth2 و openid connect.

نطاق المزود

أعتقد حقًا أنه يجب أن يكون هناك نوع من المعرف الثانوي في جدول associated_groups يمكن لقائد الموقع قراءته. هناك عدد من السيناريوهات التي قد لا يكون فيها اسم المجموعة بمفرده كافيًا. هناك احتمال حدوث تضارب في الأسماء عبر المفهوم المكافئ لكل خدمة، على سبيل المثال:

  1. إدارة مجموعات Google متعددة النطاقات (يمكنك أيضًا وجود نطاقات متعددة في مساحة عمل واحدة)
  2. إدارة مجموعات Github متعددة المنظمات
  3. إدارة أدوار Discord متعددة الخوادم
    إلخ

يمكننا تغيير domain إلى namespace ربما. يمكننا تضمينه في name للمجموعة، لكن هل سيعطينا ذلك أي مزايا؟ قد يكون من المفيد الاستعلام عن «النطاق» أو «الفضاء الاسمى» في وقت ما. نعم، ربما يكون namespace أفضل من domain.

السبب في ضرورة أن يكون «قابلًا للقراءة من قبل المسؤول» هو أنه يُستخدم في التسمية التي يراها المسؤول في واجهة مستخدم المجموعات، جزئيًا لأغراض إزالة الغموض.

أنا أفكر فيما إذا كان من المنطقي محاولة تخزين provider_id هنا أيضًا (إذا كان موجودًا). قد يكون ذلك مفيدًا في المستقبل ربما.

نعم، أتفق مع كل هذا، وشكرًا على النصائح. سأجرب هذا الجزء ويمكننا مناقشته أكثر في طلب السحب (PR).

4 إعجابات

@david لقد قمت للتو بدفع بعض التحديثات على هذا الأمر، بما في ذلك:

  • DistributedMutxes و in_batches في group_associated_group
  • اختبارات القبول (كان لدينا بالفعل rspec)

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

5 إعجابات

ربما يستحق الأمر تعيينه كغير مسودة مؤقتًا؟

إعجاب واحد (1)

مرحبًا @angus! أنا مهتم بمعرفة ما إذا كانت هناك أي تقدم إضافي في هذا الصدد؟ أنا مهتم جدًا بسلوك “الصرامة” البسيط، كما أفهمه، وبما أننا نتحكم في مزود Oauth2/OpenID Connect الخاص بنا، فلا أقلق بشأن حالة “التفويض الثانوي”. هل هناك أي فرصة لأن يتم تنفيذ شيء من هذا القبيل في وقت أقرب؟

إذا كان ذلك يساعد، فإن بيئتنا موثقة هنا: https://fedoraproject.org/wiki/Infrastructure/Authentication، وقد قمت بتكوين DIscourse لطلب نطاق oauth2 openid profile email https://id.fedoraproject.org/scope/groups.

بشكل أساسي، كل ما أريده هو:

  • ترك مستوى الثقة ومجموعات الموظفين كما هي
  • إضافة المستخدم إلى أي مجموعات Discourse موجودة في القائمة من SSO التي توجد في القائمة إذا لم يكن هناك بالفعل
  • إزالة المستخدم من أي مجموعات يكون فيها ولا توجد في القائمة

أعترف بحرية أنني لا أفهم جميع التفاصيل الدقيقة… هل هناك تعقيد لا أفهمه؟

إعجابَين (2)

لقد خصصت وقتًا هذا الأسبوع للعمل على هذا يا مات. سأقدم تحديثًا الأسبوع المقبل، على الأرجح في طلب السحب (PR) على GitHub.

إعجابَين (2)

رائع — شكرًا جزيلاً لك. لا أقصد أن أُلحّ، لكن دعم Discourse أشار إلى أن النشر في هذا الموضوع هو أفضل طريقة لمعرفة الوضع الحالي. :slight_smile:

أنا متحمس لعملك في هذا، لأنه يوجد الكثير مما يمكننا فعله مع مواقع Fedora على Discourse بمجرد توفر هذا، وهو أمر لا يمكننا فعله حاليًا!

إعجابَين (2)