الزاوي - كيفية التحقق من التفويض بناءً على الدور وحالات الكيان

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

ل TL ؛ DR هنا هو العرض التوضيحي والرمز المستخدم في العرض التوضيحي.

اعتمادات الصورة

ما هيك هو إذن كيان الدولة؟

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

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

الخطوة 1: الحصول على أدوار المستخدم الحالية

قم بتطبيق خدمة لاستردادها من جانب الخادم (أول مرة) أو من جلسة أو ملفات تعريف الارتباط كما تفضل للاستخدامات اللاحقة لها ، والشيء المهم هنا هو توفير قائمة المستخدم من القدرات (الأدوار).

// مثال
{
  الاسم: 'جون دو' ،
  البريد الإلكتروني: '[email protected]' ،
  الأدوار: ['البائع' ، 'seller_manager'] ، <- هذا ما يهم
  accessToken: 'alotofletersre تمثيلinganaccesstokenhahahaaa!'
  ... المزيد من الاشياء
}

في حالتي أكثر أو أقل هذا ما يبدو عليه:

// الواردات هنا ...
Injectable ()
فئة التصدير CurrentUserService {
  userSubject الخاص = ReplaySubject جديد <المستخدم> (1) ؛
  hasUser الخاص = false؛

  مُنشئ (المستخدمون الخاصون Api: UserApi) {
  }

  getUser العامة (): يمكن ملاحظته <المستخدم> {
    إذا (! this.hasUser) {
      this.fetchUser ()؛
    }
    إرجاع this.userSubject.asObservable ()؛
  }

  fetchUser () العام: void {
      this.usersApi.getCurrent () // <== http call لجلب userInfo
        .subscribe (user => {
          // يجب أن يحتوي المستخدم على أدوار مُنحت
          this.hasUser = صواب ؛
          this.userSubject.next (المستخدم)؛
          this.userSubject.complete ()؛
        } ، (خطأ) => {
          this.hasUser = false؛
          this.userSubject.error (خطأ)؛
        })؛
  }

}

الخطوة الثانية: بناء خريطة سير العمل والأذونات الخاصة بك

هذا ليس سوى رسم خرائط لما يمكننا القيام به ومن يمكنه فعل ذلك من خلال بناء شجرة مع كيانات مختلفة ودولهم الخاصة. على سبيل المثال ، دعنا نتخيل عملية البيع التالية ؛ قد يحتوي تطبيقنا على عدة أنواع من الأدوار. على سبيل المثال ، دعونا نخطط الأدوار لـ SELLER ، حلول العمارة والعميل.

لئلا نتحدث عن العملية:

  • أولاً ، يضع SELLER فرصة مبيعات من خلال تنفيذ الإجراء إضافة فرصة جديدة حتى يتم إنشاء حالة الفرصة
  • في هذه المرحلة ، يمكن للعميل & البائع إضافة المتطلبات إلى الفرصة حتى يتمكن كلاهما من تطبيق الإجراء إضافة متطلبات عند وضع المتطلبات ثم قد تتغير حالة الفرصة إلى إرسال
  • بمجرد وضع المتطلبات ، قد يرغب ARCHITECT في إضافة حل حتى يحتاج إلى إجراء: تحميل الحل وربما تتغير الحالة إلى حل
  • بمجرد تقديم الحل ، قد يرغب العميل في القبول لذلك يحتاج إلى إجراء للموافقة على الحل وستتغير الحالة إلى solution_approved

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

{
  "فرصة": {
    "addOpportunity": {"allowRoles": ["SELLER"]}}،
    "خلقت": {
      "addRequirement": {"allowRoles": ["SELLER"، "CLIENT"]}
    }،
    "قدمت": {
      "addSolution": {"allowRoles": ["ARCHITECT"]}
    }،
    "تم حلها": {
      "approveSolution": {"allowRoles": ["CLIENT"]}
    }
}

الخطوة 3: خدمة تخويل الاختيار لاستهلاك خريطة worflow والأذونات

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

// استيراد البيانات هنا
/ / بالمناسبة في الزاوي- CLI يمكننا وضع ملف JSON في
// البيئة
Injectable ()
سير العمل فئة التصدير {
  WORKFLOW_EVENTS الخاص للقراءة فقط = بيئة ['سير العمل'] ؛
  userRoles الخاص: Set ؛
  / / هل تذكر الخطوة 1؟ يتم استخدامه هنا
  مُنشئ (CurrentUserService الخاص: CurrentUserService) {
  }
  / / إرجاع منطقية يمكن ملاحظتها
  checkAuthorization العامة (المسار: أي): يمكن ملاحظتها <منطقية> {
    // نحن نقوم بتحميل الأدوار مرة واحدة فقط
   إذا (! this.userRoles) {
      إرجاع this.currentUserService.getUser ()
        .map (currentUser => currentUser.roles)
        .do (الأدوار => {
          أدوار const = role.map (role => role.name) ؛
          this.userRoles = مجموعة جديدة (أدوار) ؛
        })
        .map (role => this.doCheckAuthorization (path)) ؛
    }
    return Observable.of (this.doCheckAuthorization (path))؛
  }

  doCheckAuthorization الخاص (المسار: string []): boolean {
    if (path.length) {
      const entry = this.findEntry (this.WORKFLOW_EVENTS، path)؛
      إذا (الإدخال و الإدخال ['مسموح به الأدوار']
             && this.userRoles.size) {
        عودة الدخول .permittedRoles
        .some (allowRole => this.userRoles.has (allowRole)) ؛
      }
      عودة كاذبة؛
    }
    عودة كاذبة؛
  }
/ **
 * العثور على إدخال سير العمل خريطة متكررة بناء على سلاسل المسار
 * /
findEntry الخاص (currentObject: any ، keys: string [] ، index = 0) {
    مفتاح const = المفاتيح [الفهرس] ؛
    if (currentObject [key] && index 

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

ملف 4: التوجيه

بمجرد أن نحصل على أدوار المستخدم الحالية ، وشجرة أذونات سير العمل ، وخدمة للتحقق من إذن أدوار المستخدم الحالية ، نحتاج الآن إلى وسيلة لوضع هذا على قيد الحياة ، وأفضل طريقة في الزاوي 2/4 هو التوجيه. في البداية ، كان التوجيه الذي كتبته هو توجيه سمة يتم تبديل سمة CSS للعرض ، لكن هذا قد يؤدي إلى مشاكل في الأداء نظرًا لأن المكونات المتحللة ما زالت قيد التحميل على DOM ، لذلك من الأفضل استخدام التوجيهات الهيكلية (شكرًا لزميلي Petyo Cholakov على هذا الالتقاط الجيد ، انظر الفرق هنا) لأنه يمكننا تعديل DOM للعنصر الهدف ومن المتسللين حتى نتمكن من تجنب تحميل العناصر غير المستخدمة.

Directive ({
  المحدد: '[appCanAccess]'
})
فئة التصدير CanAccessDirective تنفذ OnInit ، OnDestroy {
  Input ('appCanAccess') appCanAccess: string | خيط[]؛
  إذن خاص $: الاشتراك ؛

  منشئ (القالب الخاص ريف: TemplateRef  ،
              عرض خاصحاوية: ViewContainerRef،
              سير العمل الخاص: أحداث سير العمل) {
  }

  ngOnInit (): void {
    this.applyPermission ()؛
  }

  تطبيق خاصإصدار (): باطل
    this.permission $ = this.workflowEvents
                        .checkAuthorization (this.appCanAccess)
      .subscribe (أذن => {
        إذا (مخول) {
          this.viewContainer.createEmbeddedView (this.templateRef)؛
        } آخر {
          this.viewContainer.clear ()؛
        }
      })؛
  }

  ngOnDestroy (): void {
    this.permission $ .unsubscribe ()؛
  }

}

وأخيرا العمل الناتج

الآن لدينا كل ما نحتاجه هو الوقت لوضعهم موضع التنفيذ. لذلك في قالب HTML الخاص بنا ، الشيء الوحيد الذي يتعين علينا القيام به هو شيء يشبه الكود التالي

دعنا نفترض أن لدينا مكون عينة يتضمن كائن الفرصة:

@مكون({
  المحدد: "لوحة التسعير sp" ،
  القالب:
<من نفس العينة المكون>
    
 
 
 
 `
})
تصدير فئة SampleComponent بتنفيذ OnInit {

  Input () OpportunitiesObject: any؛
  البناء() {
  }

  ngOnInit () {
  }
}

وهذا كل شيء ، يمكن أن يكون لدينا مكون بسيط يقدم السلوك بناءً على أدوار المستخدم وحالة الكيان.

بفضل زملائي Petyo و Govind على الاهتمام بالنقد السيئ الذي وجدناه ، يمكننا إيجاد هذا الحل الذي يمكن أن يناسب احتياجاتنا تمامًا ، وآمل أن يساعدك هذا أيضًا.

يونيو 2018 ، عينة صغيرة تعمل => https://emsedano.github.io/ng-entity-state/