Projekt BISO 3 - Handbuch
[inhalt]

Kanton Genf: OIDC/GINA-Benutzerprovisionierung

English Version

Diese Seite beschreibt die Genfer Umsetzung des OIDC-Auto-Provisionings. Das allgemeine Framework ist unter OIDC Auto-Provisionierung (Single-Sign-On) dokumentiert. Umgesetzt in backend/logic/GeOidcUserProvisioner.php.

Kontext

Der Identity-Provider GINA authentifiziert die Genfer Benutzer. GINA liefert BISO jedoch nur die Liste der Applikationsrollen (keine feingranularen Rechte) – und diese nicht in den Front-Channel-Claims, sondern über einen userinfo-Endpoint. BISO holt die Rollen dort ab und bildet sie auf vollständig konfigurierte BISO-Benutzer ab.

Identität und Rollen

Aus dem Login/userinfo stammen:

Die Rollen werden als voller String (inkl. Präfix DIP.BISO.) gemappt; die Config-Werte tragen entsprechend den vollen Rollennamen. Die generische Rolle DIP.BISO.UTILISATEUR wird ignoriert (führt allein zu keinem Zugriff).

Beispiel einer userinfo-Antwort:

{
  "sub": "BRESSONJ",
  "firstName": "Julien",
  "lastName": "Bresson",
  "email": "julien.bresson@etat.ge.ch",
  "roles": [
    "DIP.BISO.ADMINISTRATEUR-APPLICATION",
    "DIP.BISO.GESTIONNAIRE-SECRETAIRE",
    "DIP.BISO.UTILISATEUR"
  ]
}

Feld-Mapping (GINA → Benutzer)

Die französischen Formularlabels der BISO-Benutzermaske entsprechen folgenden Benutzer-Feldern (in Genf wird Regionalstelle als Prestation beschriftet):

Formularlabel (FR) Benutzer-Feld
Personne person_id (verknüpfte Person: Name/Vorname/E-Mail)
Prestation institution_id (eine Institution)
Groupe comptes rendus protokoll_gruppe_id (eine Gruppe)
Groupe Propriétaire owner_group_id (eine Gruppe)
Autorisation du groupe = Écrire owner_group_initial_mode = 6 (Schreiben)
Autorisation d'accès par d'autres = Lire world_initial_mode = 4 (Lesen)
Langue = Français sprache_id = Französisch
Conseiller/ère ist_berater
Secrétariat sekretariat
Chef d'atelier workshopleiter
Planificateur d'atelier workshopplaner
Modifier des modules et des prestations produkte
Modifier des personnes et des institutions stammdaten
Montrer Meta-Infos show_meta_infos
Créer des utilisateurs darf_benutzer_anlegen

Profile

Gemeinsam für alle Profile: Sprache = Französisch, owner_group_initial_mode = 6 (Schreiben), world_initial_mode = 4 (Lesen), verknüpfte Person. Die Person wird zudem je Seat als aktiver Mitarbeiter der Prestation/Institution erfasst (find-or-create, deaktivierte werden reaktiviert) — das Personen-Feld im Benutzer-Formular zeigt nur Mitarbeiter der Institution an.

Profil (Applikationsrolle) Benutzer/Seats Gruppen Rollen/Rechte Prestation / Groupe comptes rendus / Groupe Propriétaire
ADMINISTRATEUR-APPLICATION 1 alle Gruppen alle Rollen-Flags; alle Rechte ausser darf_benutzer_anlegen OSP
GESTIONNAIRE-SECRETAIRE 1 OSP + die 8 Service-Gruppen Conseiller, Secrétariat, Chef d'atelier, Planificateur; Rechte produkte/stammdaten/show_meta_infos OSP
GESTIONNAIRE-CONSEILLER ein Benutzer pro Service OSP + Service-Gruppe Conseiller, Chef d'atelier, Planificateur (kein Secrétariat); Rechte produkte/stammdaten/show_meta_infos der jeweilige Service

Kombination: Die Profile wirken additiv — ein OSP-Seat plus je ein Conseiller-Seat pro Service. ADMINISTRATEUR-APPLICATION und GESTIONNAIRE-SECRETAIRE teilen sich den OSP-Seat (beide zeigen auf die OSP-Institution; das Admin-Profil ist das Superset und gewinnt dort).

Mehrere Services: ein Benutzer pro Service

Ein GESTIONNAIRE-CONSEILLER mit mehreren PRESTATION-*-Rollen erhält je Service einen eigenen Benutzer (Seat) auf demselben Login. Beim Login zeigt BISO dann das bestehende Mehrfach-Benutzer-Auswahlfenster, in dem der Benutzer den gewünschten Service wählt (unterschieden über die Institution/Prestation). Siehe Seat-Konzept.

Synchronisation und Deaktivierung

Bei jedem Login (wenn oidc.sync_existing = true):

Konfiguration

1. Apache (mod_auth_openidc)

mod_auth_openidc muss den Handshake durchführen und das Access-Token an PHP weiterreichen (damit OIDC_access_token für den userinfo-Aufruf verfügbar ist):

OIDCPassClaimsAs both
OIDCPassAccessToken On

Vorlage: docker/dev/apache-000-oidc.conf.

2. config.php – Basis

Config::set('oidc.auto_provision', true);  // aktiviert das Provisioning (remote_user_auth nicht nötig)
Config::set('oidc.sync_existing', true);
Config::set('oidc.userinfo_endpoint', 'https://ssorec.geneveid.ch/ginasso/oauth2/userinfo');
Config::set('oidc.roles_claim', 'roles');

3. config.php – Gruppen-/Institutions-Mapping (nach ID)

Rollen werden nach ID auf BISO-Gruppen und -Institutionen abgebildet. Die IDs stammen aus der GE-Produktions-Datenbank. Jeder Service und OSP müssen dort sowohl als Gruppe als auch als Institution existieren.

Die dafür benötigten IDs liefern die folgenden beiden Abfragen. Die Spalte anzahl_benutzer zeigt, wie viele Benutzer den Eintrag nutzen — so lassen sich die aktiv genutzten Einträge erkennen. Beide Abfragen liegen auch als ausführbare Scripte unter sql/ bereit.

Gruppen (für osp_gruppe_id und die gruppe_id je Service):

SELECT g.id, g.name, count(bg.benutzer_id) AS anzahl_benutzer
FROM gruppe g
    LEFT JOIN benutzer_gruppe bg ON bg.gruppe_id = g.id
GROUP BY g.id, g.name
ORDER BY g.name;

Institutionen / Prestations (für osp_institution_id und die institution_id je Service; der Filter ist_bsb grenzt auf die OSP-Prestations ein — ohne ihn erscheinen auch alle Schulen):

SELECT i.id, i.name, i.aktiv,
       count(b.id) FILTER (WHERE b.deleted IS NULL AND b.aktiv) AS anzahl_benutzer
FROM v_institution i
    LEFT JOIN benutzer b ON b.institution_id = i.id
WHERE i.ist_bsb
GROUP BY i.id, i.name, i.aktiv
ORDER BY i.name;
Config::set('oidc.ge.osp_gruppe_id', 0);       // OSP: Gruppe
Config::set('oidc.ge.osp_institution_id', 0);  // OSP: Institution (Prestation)

Config::set('oidc.ge.services', [
    // '<volle ROLLE, GROSSBUCHSTABEN>' => ['gruppe_id' => <int>, 'institution_id' => <int>],
    'DIP.BISO.PRESTATION-CYCLE-D-ORIENTATION' => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-ACCESSII'            => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-PREQUALIFIANT'       => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-CFP-COMMERCE'        => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-ECG'                 => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-COLLEGE'             => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.ENSEIGNEMENT-SPECIALISE'        => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-ETUDIANTS'           => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-TOUT-PUBLIC'         => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-VIAMIA'              => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-CHAMP-DOLLON'        => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-FEMME-ET-EMPLOI'     => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-TREMPLIN-JEUNES'     => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-EVASCOL'             => ['gruppe_id' => 0, 'institution_id' => 0],
    'DIP.BISO.PRESTATION-PRO-APPRENTIS'       => ['gruppe_id' => 0, 'institution_id' => 0],
]);

// Die 8 Service-Rollen, in deren Gruppen das SECRETAIRE-Profil Mitglied ist:
Config::set('oidc.ge.secretaire_service_roles', [
    'DIP.BISO.PRESTATION-ETUDIANTS', 'DIP.BISO.PRESTATION-TOUT-PUBLIC', 'DIP.BISO.PRESTATION-VIAMIA',
    'DIP.BISO.PRESTATION-CHAMP-DOLLON', 'DIP.BISO.PRESTATION-FEMME-ET-EMPLOI',
    'DIP.BISO.PRESTATION-TREMPLIN-JEUNES', 'DIP.BISO.PRESTATION-EVASCOL', 'DIP.BISO.PRESTATION-PRO-APPRENTIS',
]);

4. Log-Kanal

Eigener oidc-Log-Kanal wie im allgemeinen Framework beschrieben, siehe OIDC Auto-Provisionierung – Logging.

Benutzer aus Excel-Liste importieren

Benutzer können vorab aus der GINA-Rollenliste angelegt werden, ohne dass sich jede Person zuerst einmal anmelden muss:

php backend/tools/ge_oidc_user_import.php <datei.xlsx>            # Probelauf (Default)
php backend/tools/ge_oidc_user_import.php <datei.xlsx> --do-it    # schreibt

Loginname: Standardmässig wird die Spalte Identifiant zu login.benutzername, per --login-column=<Überschrift> umstellbar. Entscheidend ist, dass die Spalte denselben Wert trägt, den GINA als sub liefert — und das ist der Identifiant (BRESSONJ), nicht die E-Mail-Adresse. Trifft die Spalte den sub nicht, wird der importierte Benutzer beim ersten SSO-Login nicht erkannt und ein zweites Konto entsteht. Zur Kontrolle meldet der Probelauf für die gewählte und die alternative Spalte, wie viele Werte bereits als Login existieren; die Spalte mit der Überschneidung ist die richtige.

Gross-/Kleinschreibung: Der Login-Pfad normalisiert nirgends — extractUsername() trimmt nur, findLogin() vergleicht exakt, und der Unique-Index auf login.benutzername ist case-sensitiv. Der Loginname muss den sub des IdP daher zeichengenau treffen. Das Script warnt deshalb unter Angabe der Zeile, wenn ein Wert sich von einem bestehenden Login nur in der Schreibweise unterscheidet — das ergäbe ein Zweitkonto. Korrigiert wird nichts automatisch: die Schreibweise gehört in die Quelldatei.

Das Script bildet die Rollen nicht selbst ab: es baut aus jeder Zeile die Claims, die GINA liefern würde, und übergibt sie dem bestehenden GeOidcUserProvisioner. Seats, Gruppen, Flags, verknüpfte Person und Mitarbeiter entstehen dadurch identisch zum SSO-Login. Der userinfo-Aufruf wird für den Lauf abgeschaltet (die Rollen kommen aus der Datei), oidc.sync_existing wird erzwungen.

Erwartetes Dateiformat (Sheet Collaborateurs-GINA-BISO): eine Bandzeile mit den Zellen GINA und – weiter rechts – BISO, darunter eine Kopfzeile mit Identifiant, Nom, Prénom, Adresse email. Jede Spalte zwischen GINA und BISO ist eine Rolle und muss einer Profilrolle oder einem Schlüssel aus oidc.ge.services entsprechen. Markierung ist x. Die optionale Spalte Nb de sous-profils attendus dans BISO wird gegen die berechnete Seat-Anzahl geprüft.

Vor dem ersten Schreibzugriff prüft das Script die ganze Datei und bricht bei Problemen ab, ohne etwas zu schreiben: unbekannte Rollenspalte (mit Korrekturvorschlag), doppelter Identifiant, fehlende Identität, Zeile ohne Profilrolle. Ein Service aus der Config ohne Spalte in der Datei ergibt nur eine Warnung.

Pro Person meldet der Lauf die erkannten Profil- und Servicerollen, die daraus berechneten Prestationen (ID und Name) sowie ob ein OSP-Seat entsteht. Ein OSP-Seat kommt nur aus ADMINISTRATEUR-APPLICATION oder GESTIONNAIRE-SECRETAIRE; GESTIONNAIRE-CONSEILLER vergibt die OSP-Gruppe, aber keine OSP-Prestation.

Weil der Import dasselbe Ergebnis liefern muss wie ein echter GINA-Login, warnt das Script zusätzlich, wenn eine Zeile ADMINISTRATEUR-APPLICATION trägt, aber nicht alle Service-Spalten markiert sind. GINA vergibt einem Administrator alle Service-Rollen; steht zusätzlich GESTIONNAIRE-CONSEILLER in derselben Zeile, entsteht daraus beim ersten SSO-Login pro Service ein Berater-Konto. Die Warnung nennt beide Zahlen.

Im Probelauf läuft alles in einer Transaktion, die am Schluss zurückgerollt wird. Mit --do-it wird pro Person committet — ein Fehler bei einer Person lässt die bereits verarbeiteten bestehen. Ein zweiter Lauf legt nichts neu an, sondern synchronisiert nur. Benutzer, die nicht in der Liste stehen, werden nicht angefasst.

Voraussetzung ist eine vollständige Konfiguration: oidc.ge.services muss alle Services enthalten, und Gruppen und Institutionen müssen existieren (siehe sql/).

GINA-Rollenliste

Profil-Rollen: ADMINISTRATEUR-APPLICATION, GESTIONNAIRE-SECRETAIRE, GESTIONNAIRE-CONSEILLER (jeweils mit Präfix DIP.BISO.).

Service-Rollen (PRESTATION-*): PRESTATION-CYCLE-D-ORIENTATION, PRESTATION-ACCESSII, PRESTATION-PREQUALIFIANT, PRESTATION-CFP-COMMERCE, PRESTATION-ECG, PRESTATION-COLLEGE, ENSEIGNEMENT-SPECIALISE, PRESTATION-ETUDIANTS, PRESTATION-TOUT-PUBLIC, PRESTATION-VIAMIA, PRESTATION-CHAMP-DOLLON, PRESTATION-FEMME-ET-EMPLOI, PRESTATION-TREMPLIN-JEUNES, PRESTATION-EVASCOL, PRESTATION-PRO-APPRENTIS.

Troubleshooting