Nurga all - kuidas kontrollida autoriseerimist rolli ja olemi olekute alusel

Tänapäeval on väga tavaline, et leiate teid oma rakenduses autentimise ja autoriseerimise seisukorras. Autoriseerimisteekide ja -tehnikate veebis kaevamine on lihtne alati leida lahendusi, mis pakuvad ainult rollipõhiseid volitusi, mis takistavad juurdepääsu ainult lehele, kuid peaaegu sageli juhtub, et vajate midagi muud, üksuse-riigi luba.

TL jaoks on DR siin demo ja demos kasutatud kood.

Pildikrediidid

Mida kuradit on üksuse osariigi luba?

Noh, ma arvan, et selle postituse kirjutamise ajal polnud peas midagi paremat nimetada. Kuid enam-vähem see, mida ma üritan öelda, on seotud olukordadega, kui peate praegusele kasutajale andma või tühistama võimaluse rakendada konkreetset toimingut, kui praeguse oleku olek tugineb X-olekule ja teistsugusele võimele, kui olem olek tugineb Y-olekule ja halvimaks läks siis, kui kõik see peaks asuma samal ekraanil või isegi samas komponendis. Kõik rollbaasi volitustega teegid säästavad selle probleemi jaoks.
Väsinud erinevate artiklite lugemisest, millel on rollipõhised lehed, mis takistavad lahendusi, hakkasin mõtlema ja kodeerima, ilma et mul oleks midagi silmas pidanud, ja äkki seal see oli, leidsin lihtsa, arusaadava ja väga paindliku lahenduse sedalaadi probleemide lahendamiseks ning see koosneb neljast komponendid.

  • Teenus praeguse kasutajateabe pakkumiseks (oluline on vaid viis anda kasutajarolle, mis praegusele kasutajale kuulub või kellele on antud rakenduses luba anda)
  • Üks töövoo lubade kaart (JSON-fail)
  • Lubade teenus loa kontrollimiseks
  • Direktiiv tarbimiskontrolli teenuse kasutamiseks

1. samm: hankige praegused kasutajarollid

Rakendage teenus serveri poolelt (esmakordne) või seansist või küpsistest allalaadimiseks, nagu eelistate selle edaspidiseks kasutamiseks, siin on oluline pakkuda kasutajale võimete (rollide) loendit.

// Näide
{
  nimi: 'John Doe',
  e-post: '[email protected]',
  rollid: ['müüja', 'müüja_haldur'], <- see on oluline
  accessToken: "kõik esindavad juurdepääsuvõimalusihahahaaaa!"
  ... rohkem asju
}

Minu puhul näeb see enam-vähem välja selline:

// import siia ...
@Süstitav ()
ekspordiklass CurrentUserService {
  private userSubject = uus ReplaySubject  (1);
  privaatne hasUser = vale;

  ehitaja (privaatne usersApi: UserApi) {
  }

  avalik getUser (): jälgitav  {
    if (! this.hasUser) {
      this.fetchUser ();
    }
    tagasta see.userSubject.asObservable ();
  }

  avalik fetchUser (): tühine {
      this.usersApi.getCurrent () // <== http-kõne kasutajaInfo toomiseks
        .tellimine (kasutaja => {
          // kasutaja peaks sisaldama rolle on antud
          this.hasUser = true;
          this.userSubject.next (kasutaja);
          this.userSubject.complete ();
        }, (viga) => {
          this.hasUser = false;
          this.userSubject.error (viga);
        });
  }

}

Teine samm: koostage oma töövoo ja lubade kaart

See pole midagi muud, kui kaardistada, mida saaksime teha ja kes saaksime, ehitades puu erinevate üksuste ja nende olekutega. kujutlegem näiteks järgmist müügiprotsessi; Meie rakendusel võib olla mitu rollitüüpi. Meie näite jaoks kaardistagem rollid MÜÜJAle, lahendustele ARHITEKT ja KLIENDILE.

Kõige vähem räägime protsessist:

  • Kõigepealt lisab MÜÜJA müügivõimaluse, täites toimingu Lisa uus võimalus, nii et tõenäoliselt luuakse võimaluse olek
  • Sel hetkel saavad KLIENT & MÜÜJA lisada võimalusele nõudeid, nii et mõlemad saaksid rakendada toimingut Lisa nõuded, kui nõuded on esitatud, siis võib võimaluse olek muutuda esitatud
  • Kui nõuded on esitatud, võib ARHITEKT soovida lahendust lisada, nii et ta vajab toimingut: Laadige lahendus üles ja tõenäoliselt võib olek muutuda lahendatuks
  • Kui lahendus on pakutud, võib KLIENT soovida nõustuda, nii et ta vajab lahenduse kinnitamiseks toimingut ja riik vahetaks välja lahendus_ heaks kiidetud

Me hakkame siin protsessi katkestama, vastasel juhul kasvaks see liiga palju ja see pole selle lugemise puhul nii. Nii et selle protsessi põhjal kaardistades ja eeldades, et võimalusel entiteedil on riik, mis jälgib olekut, näeks meie töövoog välja järgmine:

{
  "võimalus": {
    "addOpportunity": {"lubatudRoles": ["SELLER"]}},
    "loodud": {
      "addRequirement": {"lubatudRoolid": ["MÜÜJA", "KLIENT"]}
    },
    "esitatud": {
      "addSolution": {"lubatudRoles": ["ARCHITECT"]}
    },
    "lahendatud": {
      "approveSolution": {"lubatudRoles": ["KLIENT"]}
    }
}

3. samm: kontrollige autoriseerimisteenust, et tarbida töövoo ja lubade kaarti

Nüüd, kui protsess on kaardistatud töövoo ja lubade kaardiks, peame looma teenuse selle tarbimiseks ja kontrollima, kas kasutajal on volitused või mitte, ja see võiks välja näha järgmine:

// import avaldused siia
// muide nurk-cli-s saame JSON-faili panna
// keskkond.ts
@Süstitav ()
ekspordiklass WorkflowEvents {
  privaatne lugemise võimalus WORKFLOW_EVENTS = keskkond ['töövoog'];
  privaatkasutajaRoolid: määrake ;
  // kas mäletate 1. sammu? seda kasutatakse siin
  konstruktor (privaatne currentUserService: CurrentUserService) {
  }
  // tagastab tõeväärtuse jälgitava
  avalik checkAuthorization (tee: ükskõik): jälgitav  {
    // laadime rolle ainult üks kord
   if (! this.userRoles) {
      tagasta see.currentUserService.getUser ()
        .map (currentUser => currentUser.roles)
        .do (rollid => {
          rollid = rollid.kaart (roll => roll.nimi);
          this.userRoles = uus komplekt (rollid);
        })
        .map (rollid => this.doCheckAuthorization (tee));
    }
    naasta Observable.of (this.doCheckAuthorization (tee));
  }

  privaatne doCheckAuthorization (tee: string []): loogiline {
    if (tee pikkus) {
      const entry = this.findEntry (this.WORKFLOW_EVENTS, tee);
      if (sisestus & & sisestus ['lubatudRoolid']
             && this.userRoles.size) {
        naasta kanne.lubatudRoolid
        mõni (lubatudRole => see.userRoles.s (lubatudRool));
      }
      tagastama vale;
    }
    tagastama vale;
  }
/ **
 * Rekursiivselt leidke töövoo kaardi sisestus raja stringide põhjal
 * /
privaatne leidmineEntry (currentObject: suvaline, võtmed: string [], register = 0) {
    klahv const = klahvid [register];
    if (currentObject [võti] && register 

Põhimõtteliselt otsitakse kehtiv kirje ja kontrollitakse, kas praegused kasutajarollid on lubatudRoolidesse kaasatud.

4. toimik: direktiiv

Kui meil olid praegused kasutajarollid, töövoo lubade puu ja teenus praeguste kasutajarollide autoriseerimise kontrollimiseks, siis nüüd on vaja viisi, kuidas see ellu viia, ja nurga 2/4 jaoks on parim viis direktiiv. Algselt oli minu kirjutatud direktiiv atribuudidirektiiv, mis lülitas kuvatava CSS-i atribuudi ümber, kuid see võib tekitada jõudlusprobleeme, kuna surnud komponendid laaditakse endiselt DOM-i, seetõttu on parem kasutada struktuuridirektiive (tänu mu kolleegile Petyo Cholakovile selle hea saagi eest) , vaadake erinevust siit), kuna saame muuta sihtelemendi ja selle petjate DOM-i, et saaksime vältida kasutamata elementide laadimist.

@Direktiiv ({
  valija: '[appCanAccess]'
})
ekspordiklass CanAccessDirective rakendab OnInit, OnDestroy {
  @Input ('appCanAccess') appCanAccess: string | nöör [];
  isiklik luba $: tellimine;

  konstruktor (privaatne templateRef: TemplateRef ,
              privaatvaadeContainer: ViewContainerRef,
              private workflowEvents: WorkflowEvents) {
  }

  ngOnInit (): tühine {
    this.applyPermission ();
  }

  private applyPermission (): tühine {
    this.permission $ = this.workflowEvents
                        .checkAuthorization (this.appCanAccess)
      .tellimine (volitatud => {
        kui (volitatud) {
          this.viewContainer.createEmbeddedView (this.templateRef);
        } veel {
          this.viewContainer.clear ();
        }
      });
  }

  ngOnDestroy (): tühine {
    this.permission $ .unsubscribe ();
  }

}

Lõpuks saadud töö

Nüüd on meil kõik, mida vajame, aeg neid ellu viia. seega on meie HTML-mallis ainus asi, mida peame tegema, järgmine kood

Oletame, et meil on näidiskomponent, mis sisaldab võimaluste objekti:

@Komponent ({
  valija: sp-hinnapaneel,
  mall: `

    
      Lisage võimalus
    
 
 
 
 "
})
ekspordiklass SampleComponent rakendab OnInit {

  @Input () Võimalusobjekt: ükskõik;
  ehitaja () {
  }

  ngOnInit () {
  }
}

Ja see selleks, meil võib olla lihtne komponent, mis näitab käitumist sõltuvalt kasutaja rollidest ja olemi olekust.

Tänu oma kolleegidele Petyole ja Govindile saagi eest ja kriitikutele minu halva kodeerimise eest suutsime leida selle lahenduse, mis sobib ideaalselt meie vajadustega, loodan, et see aitab ka teid.

JUUNI 2018, väike proov töötab => https://emsedano.github.io/ng-entity-state/