Utredning om lederattributt i Cerebrum

Det utløsende behovet for denne utredningen er behovet for å ha lederroller lagret i Cerebrum, slik at disse kan brukes til tilgangsstyring. Tilgangsstyringen foretas ved at en person som har en lederrolle meldes inn i en gruppe som gir tilgang til beskyttede ressurser. Dette er beskrevet i CRB-3133. Siden håndtering av roller er nytt for Cerebrum, er det viktig at det foretas en grundig vurdering av mulige løsninger.

1   Kildesystemer

Det finnes en rekke nåværende og fremtidige behov som må tas hensyn til i utforming av rollehåndtering. Det er viktig for å unngå at man ender opp med en modell som ikke er fleksibel nok til å håndtere forskjellige roller fra flere kildesystemer.

1.1   SAP

Lederrollen i SAP er representert ved at en persons assignment (eller ansettelse) er markert med flagget "managerFlag". Om dette flagget er satt til true, så representerer det at personen er leder for den OU'en som ansettelsen er knyttet til. En OU i SAP kan kun ha 1 leder.

I tillegg til at SAP har informasjon om lederroller, finnes det også egne frittstående roller. Hver frittstående rolle har en egen id, "roleId", og en type som er gitt av attributtet "roleName". Disse rollene håndteres i dag ved å tildele affiliation i consumer_sap.py. Det som er felles for frittstående roller og lederroller er at de har et OU som mål. Siden vi har lagret OU'er som entiter i Cerebrum, så har vi muligheten til å knytte sammen OU og person, noe som i dag gjøres i tabellen person_affiliation_source.

Eksempel på person som er lederen ved stedkode 352100 og EMERITUS ved stedkode 900000:

...
"personId": 00000000,
...
"assignments": {
  "results": [
    {
        ...
        "assignmentId": "THISISALONGSTRING1",
        ...
        "locationId": 352100,
        "locationCode": "352100",
        "effectiveStartDate": "2019-11-27",
        "effectiveEndDate": "2020-12-31"
        ...
        "managerFlag": true,
        ...
    }
  ]
}
"roles": {
  "results": [
        ...
        "roleId": "THISISALONGSTRING2",
        "roleName": "EMERITUS",
        ...
        "locationId": 900000,
        "locationCode": "900000",
        "effectiveStartDate": "2019-11-27",
        "effectiveEndDate": "2020-12-31"
    ]
}

Et forslag til en tabell som kan representere SAP-roller (litt tilsvarende entity_trait):

              Table "public.sap_person_role"
   Column             |            Type             | Modifiers
----------------------+-----------------------------+-----------
 entity_id            | numeric(12,0)               | not null
 person_id            | numeric(12,0)               | not null
 target_id            | numeric(12,0)               | not null
 role                 | numeric(6,0)                | not null
Indexes:
    "sap_person_role_pk" PRIMARY KEY, btree (entity_id)
    "sap_person_role_entity_id_unique" UNIQUE CONSTRAINT, btree (entity_id)
    "sap_person_role_unique" UNIQUE_CONSTRAINT, btree (person_id, source, target_id, role)
Foreign-key constraints:
    "sap_person_role_entity_id" FOREIGN KEY (entity_id) REFERENCES entity_info(entity_id)
    "sap_person_role_person_id" FOREIGN KEY (person_id) REFERENCES person_info(person_id)
    "sap_person_role_target_id" FOREIGN KEY (target_id) REFERENCES entity_info(entity_id)
    "sap_person_role_role" FOREIGN KEY (role) REFERENCES role_code(code)

Eksempel på data: 1000, 1001, 1002, co.role_sap_leder

I stedet for å inkludere start- og slutt-dato fra SAP i tabellen, håndteres dette på andre måter, for eksempel ved å skedulere meldinger. Det vil være nødvendig å opprette konstanter for rolletyper fra kildesystemet. Derfor er role i dette forslaget en referanse til en konstant. Dessverre for oss så har ikke alle roller fra SAP noen "roleId". Mer spesifikt gjelder dette lederrollen, som kun er representert med et flagg. "roleId" fra roller som har dette kan lagres i tabellen entity_external_id. Det vil gjøre det lettere for oss å kontrolle at en rolle fra SAP har resultert i riktig rolle i Cerebrum.

1.2   DFØ-api

BOTT-ØL prosjektet er pågående og bringer med seg innføring av et nytt api, som erstatter SAP-apiet som brukes i dag. Dette nye apiet vil føre med seg endringer som vi ikke har full oversikt over ennå. Det er imidlertid forventet at roller vil fungere ganske likt som i SAP, men at noe informasjon vil flyttes fra "assignments" og "roles" over til personobjektet. Lederflagget er et av attributtene det foreløpig planlegges å flytte fra "assignments" til "person". For Cerebrum har ikke akkurat dette noen stor praktisk betydning sett bort i fra at uthentingen av attributtet er blant funksjonaliteten som vil måtte reimplementeres ved overgangen til DFØ-apiet.

Planene for prosjektet inkluderer også innføring av flere typer roller som må håndteres. Det er planlagt innføring av såkalte yrkeskoder, som er en kategorisering av stillinger. En slik kategori kan for eksempel være "nettredaktør". I tillegg er det behov for stillinger som ikke er knyttet opp mot en bestemt OU, men heller er generelle. Et eksempel på dette kan være rollen "arkivar" som må føre til generelle tilganger. Dette er signifikant for hvordan databasemodellen for roller skal lages. Dersom den skal kunne representere dette, må den altså støtte å legge inn roller som ikke har et spesifikt mål (hverken OU eller andre ting), altså må target_id i det foregående forslaget kunne være null.

1.3   FS

I FS kan man ha roller knyttet til ulike typer objekter. Disse objekt-typene er emne, undervisning, undervisningsaktivitet, organisasjonsenhet, evu-kurs, studieprogram, kull og kullklasse. Hvilke attributter en rolle inneholder er det som implisitt definerer hva slags rolletype det er, dvs hvilken type objekt som er målet for rollen. For eksempel vil målet til et rolle-objekt som har attributtene emnekode og aktivitetkode, slik som eksempelobjektet under, være av typen undervisningsaktivitet.

<rolle fodselsdato="010119" personnr="*****" rollenr="6" rollekode="LÆRER" dato_fra="2019-08-01 00:00:00.00" institusjonsnr="185" emnekode="ENG1100" versjonskode="1" aktivitetkode="2-1" terminkode="HØST" arstall="2019" terminnr="1" saksbehinit_opprettet="FS" dato_opprettet="2019-03-29 18:37:06.00" saksbehinit_endring="KARB" dato_endring="2019-08-11 11:49:36.00" status_publisering="J" institusjonsnr_eier="185"/>

Ingen av de nevnte objekttypene er pr dags dato representert i Cerebrum som entiteter (i motsetning til OU). Den representasjonen vi har er FS-gruppene som genereres automatisk av populate_fronter_groups.py. Disse inkluderer grupper for hver av de overnevnte objekttypene i tillegg til grupper for roller som har dem som mål. Et problem med denne fremgangsmåten er at den ignorerer skillet mellom rolle og tilganger. Det er i dette tilfellet gruppemedlemskapet som indikerer både at en person har en rolle og at vedkommende har tilgangene knyttet til denne rollen.

Siden vi ikke har FS-objektene som entititer må man løse hvordan roller skal kunne ha dem som mål. Kan man eventuelt ha et mål som ikke eksisterer i Cerebrum? En mulig løsning er at objektene tas inn i databasen vår slik at roller kan ha dem som mål. Et foreløpig utkast til generell løsning (som sikkert må bearbeides videre) er som følger:

              Table "public.person_role"
   Column             |            Type             | Modifiers
----------------------+-----------------------------+-----------
 entity_id            | numeric(12,0)               | not null
 person_id            | numeric(12,0)               | not null
 target_id            | numeric(12,0)               | not null
 role_id              | numeric(12,0)               | not null
Indexes:
    "person_role_pk" PRIMARY KEY, btree (entity_id)
    "person_role_entity_id_unique" UNIQUE CONSTRAINT, btree (entity_id)
    "person_role_unique" UNIQUE_CONSTRAINT, btree (person_id, target_id, role_id)
Foreign-key constraints:
    "person_role_entity_id" FOREIGN KEY (entity_id) REFERENCES entity_info(entity_id)
    "person_role_person_id" FOREIGN KEY (person_id) REFERENCES person_info(person_id)
    "person_role_target_id" FOREIGN KEY (target_id) REFERENCES entity_info(entity_id)
    "person_role_role_id" FOREIGN KEY (role_id) REFERENCES role_info(entity_id)

Merk: Her er det siste feltet gått fra å være en konstant i det forrige forslaget til å heller være en entitet.

              Table "public.role_info"
   Column             |            Type             | Modifiers
----------------------+-----------------------------+-----------
 entity_id            | numeric(12,0)               | not null
 source               | numeric(6,0)                | not null
 identifier           | text                        | not null
Indexes:
    "role_code_pk" PRIMARY KEY, btree (entity_id)
    "role_code_entity_id_unique" UNIQUE CONSTRAINT, btree (entity_id)
    "role_code_s_i_unique" UNIQUE CONSTRAINT, btree (source, identifier)

Eksempler: 123, co.system_fs, "LÆRER", 1234, co.system_sap, "EMERITUS", 12345, co.system_sap, "managerFlag"

                      Table "public.role_permission"
   Column             |            Type             | Modifiers
----------------------+-----------------------------+-----------
 role_id              | numeric(12,0)               | not null
 permission_set_id    | numeric(12,0)               | not null
Indexes:
    "role_permission_pk" PRIMARY KEY, btree (role_id, permission_set_id)
    "role_permission_pk_unique" UNIQUE CONSTRAINT, btree (role_id, permission)
Foreign-key constraints:
    "role_permission_role_id" FOREIGN KEY (role_id) REFERENCES role_info(entity_id)
    "role_permission_permission_set_id" FOREIGN KEY (permission_set_id) REFERENCES permission_set(entity_id)

"public.role_permission" gir oss mulighet til å fingranulere tilgangskontrollen for å oppfylle RBAC. Dette tilsvarer hvordan tilgangskontroll er gjort i bofh med auth_op_set som her vil være ekvivalent til permission_set_id. En rolle kan gi flere permission_set, og hvert permission_set kan inneholde flere tilganger. Eventuelt kan feltet permission_set_id heller inneholde en referanse til den eksisterende tabellen auth_operation_set i stedet for at man oppretter egne tabeller.

                  Table "public.fs_object"
   Column             |            Type             | Modifiers
----------------------+-----------------------------+-----------
 entity_id            | numeric(12,0)               | not null
 entity_type          | numeric(6,0)                | not null
 identifier           | text                        | not null
Indexes:
    "fs_object_pk" PRIMARY KEY, btree (entity_id)
    "fs_object_entity_id_unique" UNIQUE CONSTRAINT, btree (entity_id)
    "fs_object_identifier_unique" UNIQUE CONSTRAINT, btree (identifier)
Foreign-key constraints:
    "fs_object_object_type" FOREIGN KEY (entity_id, entity_type) REFERENCES entity_info(entity_id, entity_type)

Eksempel: 1004, co.entity_fs_object, "123,FOO101,1,1999,VÅR,1"

FS-objekter legges inn i entity_info med entity_type satt til feks co.entity_type_fs_object_undervisnings_aktivitet. Denne tabellen kan evt sløyfes ved å heller bruke den eksisterende tabellen entity_external_id til å oppbevare ekstern id.

Merk: Dersom vi finner ut at det er ønskelig å ha en fullstendig representasjon av FS-objekter i databasen, må tabellen utvides med kolonner for alle attributtene. Disse blir i såfall felter som kan være null.

1.4   Andre systemer

Det kan hende at vi på et tidspunkt må representere roller fra andre kilder. Under denne utredningen har det ikke vært tilstrekkelig med tid til å utrede andre kildesystemer enn de som er nevnt ovenfor. Dette bør det settes av mer tid til før man bestemmer seg for en generell løsning. Systemer som kan være interessante å studere er blant andre PAGA og det nye IAM-systemet som vil ta over for Cerebrum. I tillegg bør det gjøres en ny evaluering av DFØ-apiet når dette kommer i produksjon.

2   Designavgjørelser

Her følger et par momenter som har å gjøre med valg av design.

2.1   Skille mellom rolle og tilgang

Et viktig moment innen tilgangsstyring er skille av rolle og tilganger. Det betyr at vi må tenke på hvordan man vil representere at tilgangene til en rolle er blitt suspendert. Skal man sette karantene på rollen? Bør det være mulig å suspendere individuelle permissions for en person-rolle?

2.2   Subjekter

I følge RBAC kan et subjekt være "a person or automated agent". Derfor bør vi kanskje åpne opp for at andre entiteter enn personer også kan ha roller.

2.3   API-endringer

Eksemplene over viser rolletyper i eksterne systemer ikke alltid har en ID. De har derimot i de fleste tilfeller en streng eller navn som de assosieres med, feks "LÆRER", "EMERITUS". Dette kan bli problematisk for oss i tilfelle navnet endres på et senere tidspunkt. Dette gjelder også for api-endringer generelt. Hva om representasjon av "managerFlag" endres på? Hva om rolletyper i eksterne systemer får egne ID-er som er adskilt fra navnet?

3   Mulige løsninger

Denne delen tar for seg mulige tekniske løsninger for lederattributt.

3.1   Enkel løsning

Den aller enkleste løsningen som addresserer det umiddelbare behovet vårt, er å melde en person direkte inn i en ledergruppe når personen blir prosessert av consumer_sap.py dersom han/hun har lederflagget satt. For å få førstegangspopulert gruppene må man imidlertid publisere meldinger om alle ledere til consumer_sap.py, noe som kan bli en utfordring siden SAP-apiet pr dags dato ikke lar oss hente ut hvem som er leder for et OU direkte. I stedet må dette slås opp via person-objektet. Derfor kan det være nødvendig å også gjøre manuelle innmeldinger i ledergruppene.

Fordelen med denne fremgangsmåten er at det gir en lav umiddelbar utviklingskostnad, og få utviklingstimer går dermed til spille om man senere blir nødt til å utvide løsningen. I tillegg unngår vi å designe oss inn i et hjørne før vi har nok informasjon om alle mulige roller som må kunne representeres. En ulempe er at løsningen ikke gir noe skille mellom rolle og tilganger.

3.2   Roller fra SAP i egen tabell

Det lages en tabell som kun tar høyde for roller fra SAP/DFØ, men ikke andre kildesystemer. Da må tabellen eventuelt suppleres med flere tabeller i tillegg dersom roller fra andre kildesystemer også skal importeres. Det er usikkert om den kortsiktige fordelen av å ha et litt enklere design veier opp for at vi på sikt kan bli nødt til å gjøre endringer.

3.3   Generell løsning

Vi forsøker å lage en løsning som er generell nok til å håndtere de fleste roller som vi tror kan bli nødvendig i fremtiden. Denne løsningen vil bli komplisert for å kunne reflektere kompleksiteten som tross alt finnes i organisasjoner som har roller. Derfor bør løsningen være basert på eksisterende standardiserte modeller og best practices som feks RBAC. Å komme frem til en generell løsning som tilfredsstiller alle behov vil sannsynligvis kreve mer arbeid, diskusjon og utredning, og det vil derfor være kostbart.

Av h
Publisert 23. apr. 2020 11:32 - Sist endret 23. apr. 2020 12:17