OBS/Kostenpflichtige Module/RESTServer/Zugaenge: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
| Zeile 147: | Zeile 147: | ||
Body: {"username":"...", "password":"..."} | Body: {"username":"...", "password":"..."} | ||
Der Server führt das im Zugang hinterlegte '''Authentifizierungs-Skript''' aus (F7 in der Zugangs-Liste). Das Skript muss eine | Der Server führt das im Zugang hinterlegte '''Authentifizierungs-Skript''' aus (F7 in der Zugangs-Liste). Das Skript muss eine Prozedur ''Authenticate'' bereitstellen, die über den Writer schreibt: | ||
* ''status'' = ''1'' bei Erfolg, ''9'' bei Misserfolg | * ''status'' = ''1'' bei Erfolg, ''9'' bei Misserfolg | ||
| Zeile 155: | Zeile 155: | ||
* ''_OBS_JWT_REFRESH_ID'' (optional) löst die Ausstellung eines Refresh-Tokens aus; ''_OBS_JWT_REFRESH_EXP'' setzt dessen Lebensdauer in Minuten (Default 90 Tage) | * ''_OBS_JWT_REFRESH_ID'' (optional) löst die Ausstellung eines Refresh-Tokens aus; ''_OBS_JWT_REFRESH_EXP'' setzt dessen Lebensdauer in Minuten (Default 90 Tage) | ||
Die Signatur lautet ''procedure Authenticate(oReader: TxRestReader; oWriter: TxRestWriter);'' - Details unter [[OBS/Kostenpflichtige Module/RESTServer/Scripting|Scripting]]. Bei Erfolg signiert der Server einen Token (HS256, Issuer ''OBS REST-Server'', Lebenszeit gemäß JWT-Exp) und antwortet: | |||
{"token": "eyJhbGciOi...", | {"token": "eyJhbGciOi...", | ||
| Zeile 261: | Zeile 261: | ||
Der Fall „Gerät verloren" ist bewusst '''nicht''' am JWT-Endpunkt untergebracht: | Der Fall „Gerät verloren" ist bewusst '''nicht''' am JWT-Endpunkt untergebracht: | ||
er braucht eine fachliche Entscheidung darüber, wer das darf. Dafür gibt es das | er braucht eine fachliche Entscheidung darüber, wer das darf. Dafür gibt es das | ||
''oWriter.JwtRevoke'', das jeder gewöhnliche Endpunkt aufrufen kann: | |||
<syntaxhighlight lang="pascal" line> | <syntaxhighlight lang="pascal" line> | ||
procedure Post(oReader: TxRestReader; oWriter: TxRestWriter); | |||
begin | begin | ||
// 'session' = nur diese Sitzung, 'all' = alle Sitzungen des Subjects | |||
oWriter.JwtRevoke('all'); | |||
oWriter.Status(204); | |||
oWriter.NoBody(); | |||
end; | end; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Zeile 282: | Zeile 276: | ||
gesetzt hat. Ohne Subject lässt sich die Menge der Sitzungen nicht bestimmen, und | gesetzt hat. Ohne Subject lässt sich die Menge der Sitzungen nicht bestimmen, und | ||
der Aufruf antwortet mit 503.}} | der Aufruf antwortet mit 503.}} | ||
==Woher ein Anmelder seine Identität bekommt== | |||
'''Diese Frage entscheidet das Skript des Zugangs, nicht der Server.''' Das | |||
Authentifizierungs-Skript liegt je Zugang in <code>ra_jwt_script</code> - und | |||
damit gilt für jede Anwendung ihre eigene Regel. | |||
Das ist keine Kleinigkeit, sondern der Punkt, an dem sich zwei Anwendungen | |||
unterscheiden. Ein Beispiel aus dem Betrieb: Ein Zugang für eine | |||
Techniker-Anwendung lehnt jeden Benutzer ohne Mitarbeiterzuordnung | |||
(<code>BENUTZ.b_mitarbeiter</code>) ab - '''bewusst''', denn ein Techniker ohne | |||
Zuordnung bekäme eine leere Auftragsliste und würde den Fehler bei sich suchen. | |||
Für einen Kundenzugang wäre genau dieselbe Regel falsch: sie träfe '''jeden''' | |||
Anmelder. | |||
Wer einen zweiten Zugang für eine andere Anwendung anlegt, prüft deshalb als | |||
Erstes: Woraus besteht die Identität dieser Anmelder, und welche Ablehnung ist | |||
für sie richtig? Ein kopiertes Skript bringt die Regel der anderen Anwendung | |||
mit, und sie fällt erst auf, wenn sich der erste echte Anmelder nicht anmelden | |||
kann. | |||
==Sitzungen (RESTSRV_TOKEN)== | ==Sitzungen (RESTSRV_TOKEN)== | ||
Aktuelle Version vom 9. September 2026, 13:30 Uhr
- A Preise aktualisieren
- C Personen übertragen
- E Kategorien verwalten
- G Kataloge verwalten
- I Merkliste übertragen
- K Varianten übertragen
- L Artikelvarianten übertragen
- M Referenzarten übertragen
- N Lagerbestände verwalten
- U Bestellungen einlesen
- V leere Passworte füllen
- W Update-Informationen zurücksetzen
- X Konfiguration
- Z Protokoll
Zugänge
Ein Zugang (Account) ist der Kanal, über den ein externer Konsument den REST-Server anspricht. Er bündelt API-Key, Host-Beschränkung, CORS-Konfiguration, optionale JWT-Authentifizierung und optionale mTLS-Subject-Prüfung.
Aufruf
Stammdaten -> Z Weitere Stammdaten -> REST-Server -> Zugänge
Felder
| Feld | Beschreibung |
|---|---|
| Name | Sprechender Name, erscheint im Protokoll und der Statistik |
| Aktiv | Inaktive Zugänge werden bei der Authentifizierung mit 403 abgewiesen |
| API-Key | Eindeutiger Schlüssel, den der Konsument im HTTP-Header apikey senden muss. Per Schlüsselsymbol generierbar |
| Host | Optionaler Hostname oder IP-Adresse des Konsumenten. Stimmt die anfragende IP nicht überein, wird mit 403 abgelehnt |
| CORS-Origins | Liste erlaubter Origins für Browser-Aufrufe, mehrere durch ; getrennt. Leer heisst nicht „keine Einschränkung", sondern „kein Browser-Zugriff" - siehe unten |
| mTLS-Subject | Erwartete RDN-Komponente(n) im Client-Zertifikat-Subject, Komma-/Semikolon-getrennt (alle müssen vorkommen; nur sinnvoll bei mTLS-Servern) |
| JWT | Schalter, ob für diesen Zugang ein JWT-Token nötig ist |
| JWT-Endpunkt | Pfad, über den alle Token-Vorgänge laufen: Anmelden, Erneuern und Abmelden (z.B. oauth oder meineapp/dev/auth). Verglichen wird der vollständige Pfad, mehrstufige Angaben mit / sind also erlaubt |
| JWT-Key | Geheimer Schlüssel zur Signierung/Verifikation, per Schlüsselsymbol generierbar |
| JWT-Exp | Gültigkeitsdauer eines ausgestellten Access-Tokens in Minuten. Derselbe Wert wird dem Konsumenten in der Token-Antwort als expiresIn in Sekunden mitgeteilt und steht dem Endpunkt-Skript als _OBS_JWT_EXP_SEC zur Verfügung |
API-Key
Der API-Key ist die Basis-Authentifizierung. Der Client übergibt ihn im HTTP-Header:
apikey: abcd-efgh-ijkl-mnop-qrstuvwxyz12
Wird ein Aufruf ohne apikey-Header gestellt, sucht der Server intern nach einem Zugang mit API-Key * - damit lassen sich (mit Vorsicht!) auch öffentliche Endpunkte ohne Authentifizierung realisieren.
Host-Beschränkung
Ist im Feld Host ein Wert hinterlegt, akzeptiert der Server Anfragen unter diesem Zugang nur dann, wenn die Remote-IP exakt diesem Host entspricht oder durch DNS-Auflösung auf diese IP zeigt. DNS-Auflösungen werden 5 Minuten gecached.
CORS
Soll ein Endpunkt aus einer Browser-Anwendung (Single-Page-App, JavaScript) heraus aufgerufen werden, muss das Origin der Anwendung in der CORS-Liste des Zugangs eingetragen sein.
Format der Liste:
https://app.kunde.de;https://test.kunde.de;https://localhost:8080
Ablauf:
- Browser sendet einen Preflight-Request (OPTIONS). Der REST-Server prüft das Origin gegen die globale Origin-Liste aller Accounts (Cache, TTL 60s) und antwortet mit 204 + CORS-Header, falls erlaubt.
- Der eigentliche Request wird gegen die Origin-Liste des aktuell verwendeten Zugangs geprüft.
- Stimmt das Origin nicht, wird mit 403 Origin nicht erlaubt für Account ... abgelehnt.
Der Preflight kann nicht zugangsbezogen prüfen: der Browser schickt dabei keinen apikey mit, der Zugang ist zu diesem Zeitpunkt unbekannt. Die Durchsetzung liegt deshalb auf Schritt 2, nicht auf dem Preflight. Praktische Folge: trägt ein Zugang auf dem Server ein * in seiner Liste, antwortet jeder Preflight mit Access-Control-Allow-Origin: * - auch für Zugänge mit engerer Liste. Deren eigentliche Anfrage wird trotzdem gegen die eigene Liste geprüft.
Erlaubte Methoden: GET, POST, PUT, DELETE, PATCH (systemseitig festgelegt).
Bei den Anfrage-Headern spiegelt der Server im Preflight zurück, was der Browser in Access-Control-Request-Headers angefragt hat. Damit sind eigene Header eines Projekts - Idempotency-Key, If-Match, später Upload-Offset/Upload-Length - ohne Änderung am Server nutzbar. Fragt der Preflight keine Header an, antwortet der Server mit der Liste Content-Type, apikey, Authorization, Accept.
sagt dem Browser nur, welche Header der Server versteht. Dazu kommt, dass die Zugangsdaten dieser Schnittstelle in Headern stehen (apikey, Authorization) und nicht in Cookies: es gibt also keine automatisch
mitgeschickte Berechtigung, die eine fremde Seite ausnutzen könnte.Was „leer" bedeutet
Einschränkung". Es heisst: jeder Aufruf mit Origin-Header wird mit 403 abgelehnt. Für Clients ohne Origin-Header - mobile Apps, Server,
curl - ändert das Feld gar nichts, sie werden nie gegen CORS geprüft.Daraus ergeben sich drei sinnvolle Einstellungen:
| Feld | Wirkung |
|---|---|
| leer | Kein Browser-Zugriff. Richtig für Zugänge, die nur von Apps oder Servern benutzt werden |
| konkrete Origins | Browser-Zugriff nur von diesen Seiten. Der Regelfall für Web-Anwendungen |
| * | Browser-Zugriff von überall. Wirkt zusätzlich auf die Preflight-Antwort aller anderen Zugänge - bewusst und sparsam verwenden |
Für eine Browser-Anwendung ist dieses Feld besonders wichtig, weil ihr API-Key zwangsläufig im JavaScript liegt und damit öffentlich ist. Die Origin-Liste verhindert dann, dass jemand den Key nimmt und von seiner eigenen Seite benutzt. Sie ist allerdings kein Ersatz für eine Zugriffskontrolle: wer den Key hat, umgeht CORS mit einem einzigen Aufruf ausserhalb des Browsers.
JWT-Authentifizierung
Wird ein Zugang mit aktiver JWT-Pflicht aufgerufen, sind die folgenden Phasen zu durchlaufen (Phase 3 nur bei genutztem Refresh).
Anmelden, Erneuern und Abmelden laufen über denselben Endpunkt - den im Feld JWT-Endpunkt hinterlegten Pfad. Ein eigener Endpunkt fürs Abmelden ist nicht nötig und wäre auch umständlicher: der JWT-Endpunkt greift vor der Adressauflösung und braucht deshalb weder eine Zeile in RESTSRV_ENDPOINTS noch eine Berechtigung in RESTSRV_ACCESS.
Unterschieden wird nach Verb und Token-Art:
| Aufruf | Ablauf |
|---|---|
| POST ohne Authorization | Anmelden (Skript-Methode Authenticate) |
| POST mit Bearer, Refresh-Token | Erneuern (Skript-Methode Refresh) |
| DELETE mit Bearer, Access-Token | Abmelden (kein Skript nötig) |
| POST mit Bearer, Access-Token | 401 - unverändert |
Absicht. Würde ein vorgelegtes Access-Token beim POST als Abmeldung gedeutet, würde ein Client, der seine beiden Token verwechselt, den Anwender still
abmelden - statt das klare 401 zu bekommen, an dem der Fehler auffällt.Phase 1: Token-Bezug
Der Konsument ruft den JWT-Endpunkt des Zugangs auf, z.B.:
POST https://api.meinserver.de/oauth/ Header: apikey: [API-KEY] Body: {"username":"...", "password":"..."}
Der Server führt das im Zugang hinterlegte Authentifizierungs-Skript aus (F7 in der Zugangs-Liste). Das Skript muss eine Prozedur Authenticate bereitstellen, die über den Writer schreibt:
- status = 1 bei Erfolg, 9 bei Misserfolg
- error = Fehlertext (bei status=9)
- _OBS_JWT_ID, _OBS_JWT_SUBJECT, _OBS_JWT_AUDIENCE für die JWT-Claims (optional)
- _OBS_JWT_CLAIM_<name> für beliebige Custom-Claims (z.B. Mandant tenant, Rollen roles); im Folge-Skript lesbar
- _OBS_JWT_REFRESH_ID (optional) löst die Ausstellung eines Refresh-Tokens aus; _OBS_JWT_REFRESH_EXP setzt dessen Lebensdauer in Minuten (Default 90 Tage)
Die Signatur lautet procedure Authenticate(oReader: TxRestReader; oWriter: TxRestWriter); - Details unter Scripting. Bei Erfolg signiert der Server einen Token (HS256, Issuer OBS REST-Server, Lebenszeit gemäß JWT-Exp) und antwortet:
{"token": "eyJhbGciOi...",
"accessToken": "eyJhbGciOi...",
"expiresIn": 28800,
"serverTime": "2026-06-29T15:30:12+02:00"}
Zum Aufbau der Antwort:
- token und accessToken enthalten denselben Token. accessToken ist der Name, den die meisten Client-Bibliotheken erwarten; token bleibt für bestehende Konsumenten erhalten.
- expiresIn ist die Gültigkeit in Sekunden (JWT-Exp × 60).
- Alle weiteren Felder, die das Authenticate-Skript zurückgibt, werden mit ausgeliefert - mit Ausnahme von status und den _OBS_-Steuerfeldern. So kann das Skript z.B. eine Benutzer-Id, den Mandanten oder Rollen direkt in die Anmelde-Antwort legen, ohne dass der Client dafür einen zweiten Aufruf braucht.
Beispiel mit Zusatzfeldern aus dem Skript:
{"benutzerId": "4711", "tenant": "nord", "roles": ["TECHNIKER"],
"token": "eyJ...", "accessToken": "eyJ...", "refreshToken": "eyJ...",
"expiresIn": 28800, "serverTime": "2026-06-29T15:30:12+02:00"}
Mit der Ausstellung legt der Server zu dem Token-Paar eine Sitzungszeile in RESTSRV_TOKEN an. Lässt sich diese Zeile nicht schreiben (Tabelle fehlt, Datenbankproblem), wird kein Token ausgeliefert: der Server antwortet mit 503 und Retry-After. Das ist bewusst von der abgelehnten Anmeldung unterschieden - die Zugangsdaten waren in Ordnung, der Versuch darf wiederholt werden.
Wird die Anmeldung abgelehnt (status = 9), antwortet der Server mit 401 Unauthorized und reicht den Text aus dem Feld error als error.message an den Client durch - der Konsument kann ihn also direkt anzeigen. Die Formulierung liegt damit beim Skript.
Phase 2: Token-Verwendung
Bei allen folgenden Aufrufen sendet der Client den Token im Authorization-Header:
GET https://api.meinserver.de/kalender/v1 Header: apikey: [API-KEY] Authorization: Bearer eyJhbGciOi...
Der Server verifiziert Signatur, Issuer, Ausstellzeitpunkt und Ablauf (Toleranz: 20 Sekunden Clock-Skew) und prüft anschliessend die Sitzung in RESTSRV_TOKEN. Im Skript stehen die Claims als Parameter _OBS_JWT_ID, _OBS_JWT_SUBJECT und _OBS_JWT_AUDIENCE zur Verfügung.
Die Sitzungsprüfung weist ein Token ab, das zu keiner Zeile gehört oder dessen Sitzung gesperrt wurde - beides mit 401 und dem Code AUTH_EXPIRED. Der Grund steht jeweils im Protokoll RESTSRV_PROTO unter TokenStore: ....
wurde, gehört zu keiner Sitzung und wird abgewiesen. Nach dem Update müssen sich
angemeldete Konsumenten also einmalig neu anmelden.Phase 3: Token erneuern (Refresh)
Soll die App lange ohne Neuanmeldung arbeiten, stellt das Authenticate-Skript zusätzlich ein Refresh-Token aus (Feld _OBS_JWT_REFRESH_ID). Zum Erneuern ruft der Client denselben JWT-Endpunkt auf, aber mit dem Refresh-Token im Authorization-Header:
POST https://api.meinserver.de/oauth/ Header: apikey: [API-KEY] Authorization: Bearer eyJhbGciOi... (Refresh-Token)
Der Server erkennt am Bearer-Token den Refresh-Fall, verifiziert das Token (Signatur, Ablauf, token_use=refresh), entwertet es in der Sitzung und ruft erst dann im selben Skript die Methode Refresh auf. Das Skript liefert nur noch die aktuellen Claims zurück - Rotation, Einmalgebrauch und Sperrliste erledigt der Server. Die Antwort ist ein rotiertes Paar {"token","accessToken","refreshToken","expiresIn","serverTime"} in derselben Sitzung.
entfernt werden. Wer sie weiterführt, rotiert zweimal: einmal im Skript, einmal im Server. Der Server sieht die Skript-Tabelle nicht, und das Skript sieht die
Sitzung nicht.Verhalten bei einem bereits benutzten Refresh-Token:
| Lage | Antwort | Wirkung auf die Sitzung |
|---|---|---|
| Erneute Vorlage innerhalb von 60 Sekunden | 401 AUTH_EXPIRED | Keine. Das ist der Normalfall einer App, deren parallele Anfragen gleichzeitig in den Refresh laufen - eine gewinnt, die anderen sollen die vom Gewinner gespeicherten Token benutzen |
| Erneute Vorlage später | 401 AUTH_EXPIRED | Die gesamte Sitzung wird gesperrt. Das Token wurde kopiert oder wiederholt, ohne dass ein Gewinner es noch brauchen könnte; der Vorfall wird mit IP protokolliert |
Details und ein vollständiges Beispiel siehe Scripting.
Phase 4: Abmelden (Logout)
Abgemeldet wird mit einem DELETE auf denselben JWT-Endpunkt, das Access-Token im Authorization-Header:
DELETE https://api.meinserver.de/oauth/ Header: apikey: [API-KEY] Authorization: Bearer eyJhbGciOi... (Access-Token)
HTTP/1.1 204 No Content
Der Server sperrt die ganze Sitzung - also auch die noch nicht abgelaufenen Access-Token aller vorherigen Erneuerungen. Ein Skript ist dafür nicht nötig: es gibt keine Methode Logout, der Vorgang läuft komplett in der Engine. Damit funktioniert das Abmelden für jeden Zugang mit aktiver JWT-Pflicht, ohne dass am Authentifizierungs-Skript etwas geändert wird.
- Ein wiederholter Aufruf ist unschädlich und bleibt 204 - auch wenn die Sitzung längst gesperrt ist.
- Ein abgelaufenes oder ungültiges Token ergibt 401 (es wird schon bei der Prüfung abgewiesen).
- Ein Refresh-Token statt des Access-Tokens ergibt 401.
- Lässt sich die Sitzung nicht sperren, antwortet der Server 503 mit Retry-After - ein Client darf nicht glauben, er sei abgemeldet, während sein Token weiterläuft.
Alle Geräte abmelden
Der Fall „Gerät verloren" ist bewusst nicht am JWT-Endpunkt untergebracht: er braucht eine fachliche Entscheidung darüber, wer das darf. Dafür gibt es das oWriter.JwtRevoke, das jeder gewöhnliche Endpunkt aufrufen kann:
procedure Post(oReader: TxRestReader; oWriter: TxRestWriter);
begin
// 'session' = nur diese Sitzung, 'all' = alle Sitzungen des Subjects
oWriter.JwtRevoke('all');
oWriter.Status(204);
oWriter.NoBody();
end;gesetzt hat. Ohne Subject lässt sich die Menge der Sitzungen nicht bestimmen, und
der Aufruf antwortet mit 503.Woher ein Anmelder seine Identität bekommt
Diese Frage entscheidet das Skript des Zugangs, nicht der Server. Das
Authentifizierungs-Skript liegt je Zugang in ra_jwt_script - und
damit gilt für jede Anwendung ihre eigene Regel.
Das ist keine Kleinigkeit, sondern der Punkt, an dem sich zwei Anwendungen
unterscheiden. Ein Beispiel aus dem Betrieb: Ein Zugang für eine
Techniker-Anwendung lehnt jeden Benutzer ohne Mitarbeiterzuordnung
(BENUTZ.b_mitarbeiter) ab - bewusst, denn ein Techniker ohne
Zuordnung bekäme eine leere Auftragsliste und würde den Fehler bei sich suchen.
Für einen Kundenzugang wäre genau dieselbe Regel falsch: sie träfe jeden
Anmelder.
Wer einen zweiten Zugang für eine andere Anwendung anlegt, prüft deshalb als Erstes: Woraus besteht die Identität dieser Anmelder, und welche Ablehnung ist für sie richtig? Ein kopiertes Skript bringt die Regel der anderen Anwendung mit, und sie fällt erst auf, wenn sich der erste echte Anmelder nicht anmelden kann.
Sitzungen (RESTSRV_TOKEN)
Je ausgestelltem Token-Paar entsteht eine Zeile. Sie enthält die Sitzung, die Zeilenkennung, das Subject, die beiden Laufzeiten, IP und Trace-ID der Ausstellung sowie die Kennzeichen „verbraucht" und „gesperrt".
- Rotation und Widerruf sind getrennt. Ein Refresh entwertet nur das Refresh-Token; der zugehörige Access-Token bleibt bis zu seinem Ablauf gültig. Sonst bekämen parallel laufende Anfragen ein 401, obwohl niemand abgemeldet ist.
- Die Sitzung überlebt die Rotation. Alle Paare einer Anmeldung tragen dieselbe Sitzungskennung - deshalb kann ein Logout sie gemeinsam sperren.
- Aufräumen läuft automatisch beim Serverstart und danach höchstens stündlich. Gelöscht wird nur, was nichts mehr entscheiden kann: Zeilen, deren Access- und Refresh-Laufzeit vorbei sind. Eine verbrauchte Zeile bleibt bis dahin stehen, weil sonst die Erkennung eines später vorgelegten gestohlenen Refresh-Tokens verloren ginge.
- Kein Eingriff nötig. Die Tabelle wird nicht gepflegt; sie ist Diagnose-Material. Alle Abweisungen stehen als TokenStore: ... in RESTSRV_PROTO.
mTLS-Subject-Prüfung
Wird im Feld mTLS-Subject ein Wert hinterlegt, prüft der Server zusätzlich zur Standard-mTLS-Validierung, ob der Subject-DN des (CA-validierten) Client-Zertifikats die hinterlegten RDN-Komponenten enthält. Verglichen wird jede Komponente vollständig (kein Substring-Match) und case-insensitiv; mehrere Komponenten werden durch Komma oder Semikolon getrennt und müssen alle im Subject vorkommen.
Beispiele für Subject-Strings im Cert (OneLine-DN):
/C=DE/O=Kunde GmbH/CN=client-prod.kunde.de /C=DE/O=Kunde GmbH/CN=client-test.kunde.de
Mit CN=client-prod.kunde.de im Feld wird genau das prod-Cert akzeptiert, das test-Cert mit 401 abgewiesen. Da jede RDN-Komponente vollständig verglichen wird, matcht z.B. CN=client1 nicht fälschlich auch CN=client10. Mehrere Bedingungen lassen sich mit Komma/Semikolon kombinieren, z.B. O=Kunde GmbH;CN=client-prod.kunde.de.
Listen-Funktionen
In der Zugänge-Liste (allein):
- Einfg - neuer Zugang
- Return - Zugang bearbeiten
- F4 - Sortierung
- F7 - JWT-Authentifizierungs-Skript bearbeiten
- F8 - Statistik für den Zugang
Aus der Endpunkt-Liste via F6 geöffnet (Auswahl für Berechtigung):
- F2 - markierte Zugänge dem Endpunkt zuordnen
- F5 - Zugang in der Liste markieren / Markierung aufheben