OBS/Kostenpflichtige Module/RESTServer/Scripting: Unterschied zwischen den Versionen
(Die Seite wurde neu angelegt: „{{Kostenpflichtige Module}} =Anleitung für Endpunkt-Scripte= Anfragen an einen Endpunkt werden (nach erfolgreicher Authentifizierung und Authorisierung) an das hinterlegte Script weitergegeben und müssen dort beantwortet werden. Gibt es einen Fehler oder wird die Anfrage nicht korrekt bearbeitet wird ein Fehlercode (HTML-Fehlercode) zurückgegeben. =Sicherheit= Da hier ein Zugriff von außen ermöglicht wird ist es sehr wichtig, dass Übergabeparame…“) |
Keine Bearbeitungszusammenfassung |
||
| (3 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
{{Kostenpflichtige Module}} | {{Kostenpflichtige Module}} | ||
=Anleitung für Endpunkt- | =Anleitung für Endpunkt-Skripte= | ||
Jede Anfrage an einen Endpunkt wird nach erfolgreicher Authentifizierung und Autorisierung an das hinterlegte Skript weitergegeben. Das Skript ist in Object Pascal geschrieben und greift auf die OBS-Bibliothek (DB-Zugriff, Hilfsfunktionen) zu. | |||
= | ==Sicherheitshinweise== | ||
{{Hinweis|Endpunkt-Skripte verarbeiten Daten aus dem Internet. Übergabeparameter dürfen niemals ungeprüft in SQL-Anweisungen eingebaut werden. Werte immer über ''DB_SQLVal'' bzw. Parameter-Bindings absichern, Eingabewerte gegen Whitelists prüfen.}} | |||
* Eingaben gegen erwartete Werte prüfen (z.B. Pflichtfelder, erlaubte Typen, Wertebereiche). | |||
* Nur das zurückgeben, was der Konsument wirklich braucht - keine internen IDs, keine Sys-Felder, keine Passwörter. | |||
* Bei Fehlern keine internen Details an den Konsumenten zurückgeben; stattdessen schreibt der Server ohnehin Detail-Einträge in '''RESTSRV_PROTO'''. | |||
==Methoden-Signatur== | |||
function | |||
begin | Pro HTTP-Methode wird im Skript eine gleichnamige Funktion implementiert. Der Server ruft genau die Funktion auf, die zur Methode des eingehenden Requests passt. | ||
[ | |||
function Get (oParams: TStrings; oBody: TJSONObject): string; | |||
function Post (oParams: TStrings; oBody: TJSONObject): string; | |||
function Put (oParams: TStrings; oBody: TJSONObject): string; | |||
function Delete(oParams: TStrings; oBody: TJSONObject): string; | |||
function Patch (oParams: TStrings; oBody: TJSONObject): string; | |||
Nicht implementierte Methoden liefern automatisch ''405''-ähnliche Fehler über die generische Skript-Antwort. | |||
==Parameter== | |||
===oParams (TStrings)=== | |||
Enthält alle Query-Parameter, POST-Parameter (form-urlencoded) sowie die durchgereichten HTTP-Header. Werte sind immer Strings. | |||
Filterung durch den Server: | |||
* Geblockte Header werden nicht durchgereicht: ''authorization'', ''cookie'', ''proxy-authorization'', ''x-forwarded-for'', ''x-real-ip'', ''apikey'', ''api_key''. | |||
* Parameter mit Präfix '''_OBS_''' können von aussen nicht gesetzt werden; sie sind für interne Werte reserviert. | |||
* Werte werden auf max. 1024 Zeichen begrenzt. | |||
* Null-Bytes und Steuerzeichen (ausser Tab, CR, LF) werden entfernt. | |||
Zugriff im Skript: | |||
if (not Empty(oParams.Values['kundennr'])) then begin | |||
cKundenNr := oParams.Values['kundennr']; | |||
end; | end; | ||
===oBody (TJSONObject)=== | |||
begin | |||
Enthält den Request-Body, sofern dieser ein gültiges JSON-Objekt ist. Bei leerem oder nicht-JSON-Body wird ''nil'' übergeben. | |||
cUser := ''; | |||
cPass := ''; | |||
if (Assigned(oBody)) then begin | |||
oVal := oBody.GetValue('username'); | |||
if (Assigned(oVal)) then begin | |||
cUser := oVal.Value; | |||
end; | |||
oVal := oBody.GetValue('password'); | |||
if (Assigned(oVal)) then begin | |||
cPass := oVal.Value; | |||
end; | |||
end; | end; | ||
Maximale Body-Grösse: 10 MB. | |||
begin | |||
===Reservierte _OBS_-Parameter=== | |||
Bei aktiver JWT-Authentifizierung stehen die Token-Claims als Parameter zur Verfügung: | |||
{| class="wikitable" | |||
! Parameter !! Inhalt | |||
|- | |||
| _OBS_JWT_ID || JWT-Id (''jti''-Claim) - typisch die User-Id | |||
|- | |||
| _OBS_JWT_SUBJECT || Subject (''sub''-Claim) - typisch Benutzername | |||
|- | |||
| _OBS_JWT_AUDIENCE || Audience (''aud''-Claim) - typisch Mandant / Rolle | |||
|} | |||
Diese Werte werden vom Server gesetzt und können vom Skript für Berechtigungs- und Mandantenprüfungen verwendet werden. | |||
==Rückgabe== | |||
Die Rückgabe ist immer ein '''JSON-String'''. Wird ein leerer String zurückgegeben, antwortet der Server automatisch mit ''{}''. Der Server setzt Content-Type auf ''application/json; charset=utf-8'' und Status auf 200, sofern das Skript nicht selbst einen Fehler signalisiert. | |||
Einfaches Beispiel: | |||
<syntaxhighlight lang="pascal" line> | |||
function Get(oParams: TStrings; oBody: TJSONObject): string; | |||
var oRes: TJSONObject; | |||
begin | |||
oRes := TJSONObject.Create(); | |||
try | |||
oRes.AddPair('wert_string', '123'); | |||
oRes.AddPair('wert_int' , 456); | |||
result := oRes.ToJSON(); | |||
finally | |||
MyFreeAndNil(oRes); | |||
end; | |||
end; | |||
</syntaxhighlight> | |||
==JWT-Authentifizierungs-Skript== | |||
Bei Zugängen mit aktiver JWT-Pflicht wird über den JWT-Endpunkt das im Zugang hinterlegte Authentifizierungs-Skript aufgerufen (F7 in der Zugänge-Liste). Das Skript muss eine Methode ''Authenticate'' bereitstellen: | |||
<syntaxhighlight lang="pascal" line> | |||
function Authenticate(oParams: TStrings; oBody: TJSONObject): string; | |||
var oRes : TJSONObject; | |||
cUser : string; | |||
cPass : string; | |||
begin | |||
oRes := TJSONObject.Create(); | |||
try | |||
// Eingangsdaten lesen (Body oder Query-Param) | |||
cUser := ''; | |||
cPass := ''; | |||
if (Assigned(oBody)) then begin | |||
oVal := oBody.GetValue('username'); | |||
if (Assigned(oVal)) then begin | |||
cUser := oVal.Value; | |||
end; | |||
oVal := oBody.GetValue('password'); | |||
if (Assigned(oVal)) then begin | |||
cPass := oVal.Value; | |||
end; | |||
end; | |||
// Prüfung gegen eigene Tabelle, Hash-Verfahren, LDAP, ... | |||
if (PasswortPasst(cUser, cPass)) then begin | |||
oRes.AddPair('status' , 1); | |||
oRes.AddPair('_OBS_JWT_ID' , cUser); | |||
oRes.AddPair('_OBS_JWT_SUBJECT', cUser); | |||
oRes.AddPair('_OBS_JWT_AUDIENCE', 'mandant1'); | |||
end else begin | |||
oRes.AddPair('status', 9); | |||
oRes.AddPair('error' , 'Login fehlgeschlagen'); | |||
end; | |||
result := oRes.ToJSON(); | |||
finally | |||
MyFreeAndNil(oRes); | |||
end; | |||
end; | |||
</syntaxhighlight> | |||
Rückgabewerte: | |||
{| class="wikitable" | |||
! status !! Bedeutung | |||
|- | |||
| 1 || Erfolgreich, der Server erzeugt aus den ''_OBS_JWT_*''-Werten einen Token (HS256) | |||
|- | |||
| 9 || Misserfolg, der Server antwortet mit 403 und protokolliert den Wert von ''error'' | |||
|} | |||
==Fehlerbehandlung im Skript== | |||
Tritt im Skript eine Exception auf oder schlägt die Syntax-Prüfung fehl, antwortet der Server mit 500 ''Interner Fehler'' und protokolliert die Detail-Meldung in '''RESTSRV_PROTO''' (mit Skript-Fehlertext). Der Konsument sieht keine internen Details. | |||
Will das Skript einen spezifischen Fehler an den Konsumenten zurückgeben, ist eine eigene JSON-Struktur zu liefern: | |||
=Parameter= | <syntaxhighlight lang="pascal" line> | ||
function Post(oParams: TStrings; oBody: TJSONObject): string; | |||
var oRes: TJSONObject; | |||
begin | |||
oRes := TJSONObject.Create(); | |||
try | |||
if (Empty(oParams.Values['kundennr'])) then begin | |||
oRes.AddPair('status', 'error'); | |||
oRes.AddPair('msg' , 'Parameter ''kundennr'' fehlt'); | |||
result := oRes.ToJSON(); | |||
exit; | |||
end; | |||
// ... | |||
finally | |||
MyFreeAndNil(oRes); | |||
end; | |||
end; | |||
</syntaxhighlight> | |||
==Skript-Cache== | |||
Der Server cached das kompilierte Skript pro Endpunkt. Änderungen am Skript über F7 sind sofort wirksam - der Server erkennt die Änderung am ''sys_date'' und compiliert neu. | |||
= | ==Verfügbare Bibliotheken== | ||
Im Skript können alle OBS-Standard-Bibliotheken verwendet werden. Typische Einstiegspunkte: | |||
* ''Base.Tools'' - String-, Datum-, IIF-, Empty-Helper | |||
* ''Base.DB'' / ''Base.xQuery'' - Datenbank-Operationen, ''DB_SQLVal'', ''DB_SOpen'', ''DB_LSeek'' | |||
* ''Base.qSqlReg'' - ''qSqlInit'', ''qSet'', ''SaveData'' | |||
* ''System.JSON'' - JSON-Objekte und -Arrays | |||
* ''Base.ToolsConst'' - Konstanten wie ''CRLF'', ''SINGELQUOTE'', ''EMPTY_DATE'' | |||
Konkrete Beispiele: | |||
* [[OBS/Kostenpflichtige Module/RESTServer/Beispiel1|Beispiel: Daten-Abruf mit JWT]] | |||
* [[OBS/Kostenpflichtige Module/RESTServer/Beispiel2|Beispiel: Datensatz anlegen mit JSON-Body]] | |||
Aktuelle Version vom 19. Mai 2026, 07:15 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
Anleitung für Endpunkt-Skripte
Jede Anfrage an einen Endpunkt wird nach erfolgreicher Authentifizierung und Autorisierung an das hinterlegte Skript weitergegeben. Das Skript ist in Object Pascal geschrieben und greift auf die OBS-Bibliothek (DB-Zugriff, Hilfsfunktionen) zu.
Sicherheitshinweise
- Eingaben gegen erwartete Werte prüfen (z.B. Pflichtfelder, erlaubte Typen, Wertebereiche).
- Nur das zurückgeben, was der Konsument wirklich braucht - keine internen IDs, keine Sys-Felder, keine Passwörter.
- Bei Fehlern keine internen Details an den Konsumenten zurückgeben; stattdessen schreibt der Server ohnehin Detail-Einträge in RESTSRV_PROTO.
Methoden-Signatur
Pro HTTP-Methode wird im Skript eine gleichnamige Funktion implementiert. Der Server ruft genau die Funktion auf, die zur Methode des eingehenden Requests passt.
function Get (oParams: TStrings; oBody: TJSONObject): string; function Post (oParams: TStrings; oBody: TJSONObject): string; function Put (oParams: TStrings; oBody: TJSONObject): string; function Delete(oParams: TStrings; oBody: TJSONObject): string; function Patch (oParams: TStrings; oBody: TJSONObject): string;
Nicht implementierte Methoden liefern automatisch 405-ähnliche Fehler über die generische Skript-Antwort.
Parameter
oParams (TStrings)
Enthält alle Query-Parameter, POST-Parameter (form-urlencoded) sowie die durchgereichten HTTP-Header. Werte sind immer Strings.
Filterung durch den Server:
- Geblockte Header werden nicht durchgereicht: authorization, cookie, proxy-authorization, x-forwarded-for, x-real-ip, apikey, api_key.
- Parameter mit Präfix _OBS_ können von aussen nicht gesetzt werden; sie sind für interne Werte reserviert.
- Werte werden auf max. 1024 Zeichen begrenzt.
- Null-Bytes und Steuerzeichen (ausser Tab, CR, LF) werden entfernt.
Zugriff im Skript:
if (not Empty(oParams.Values['kundennr'])) then begin
cKundenNr := oParams.Values['kundennr'];
end;
oBody (TJSONObject)
Enthält den Request-Body, sofern dieser ein gültiges JSON-Objekt ist. Bei leerem oder nicht-JSON-Body wird nil übergeben.
cUser := ;
cPass := ;
if (Assigned(oBody)) then begin
oVal := oBody.GetValue('username');
if (Assigned(oVal)) then begin
cUser := oVal.Value;
end;
oVal := oBody.GetValue('password');
if (Assigned(oVal)) then begin
cPass := oVal.Value;
end;
end;
Maximale Body-Grösse: 10 MB.
Reservierte _OBS_-Parameter
Bei aktiver JWT-Authentifizierung stehen die Token-Claims als Parameter zur Verfügung:
| Parameter | Inhalt |
|---|---|
| _OBS_JWT_ID | JWT-Id (jti-Claim) - typisch die User-Id |
| _OBS_JWT_SUBJECT | Subject (sub-Claim) - typisch Benutzername |
| _OBS_JWT_AUDIENCE | Audience (aud-Claim) - typisch Mandant / Rolle |
Diese Werte werden vom Server gesetzt und können vom Skript für Berechtigungs- und Mandantenprüfungen verwendet werden.
Rückgabe
Die Rückgabe ist immer ein JSON-String. Wird ein leerer String zurückgegeben, antwortet der Server automatisch mit {}. Der Server setzt Content-Type auf application/json; charset=utf-8 und Status auf 200, sofern das Skript nicht selbst einen Fehler signalisiert.
Einfaches Beispiel:
function Get(oParams: TStrings; oBody: TJSONObject): string;
var oRes: TJSONObject;
begin
oRes := TJSONObject.Create();
try
oRes.AddPair('wert_string', '123');
oRes.AddPair('wert_int' , 456);
result := oRes.ToJSON();
finally
MyFreeAndNil(oRes);
end;
end;
JWT-Authentifizierungs-Skript
Bei Zugängen mit aktiver JWT-Pflicht wird über den JWT-Endpunkt das im Zugang hinterlegte Authentifizierungs-Skript aufgerufen (F7 in der Zugänge-Liste). Das Skript muss eine Methode Authenticate bereitstellen:
function Authenticate(oParams: TStrings; oBody: TJSONObject): string;
var oRes : TJSONObject;
cUser : string;
cPass : string;
begin
oRes := TJSONObject.Create();
try
// Eingangsdaten lesen (Body oder Query-Param)
cUser := '';
cPass := '';
if (Assigned(oBody)) then begin
oVal := oBody.GetValue('username');
if (Assigned(oVal)) then begin
cUser := oVal.Value;
end;
oVal := oBody.GetValue('password');
if (Assigned(oVal)) then begin
cPass := oVal.Value;
end;
end;
// Prüfung gegen eigene Tabelle, Hash-Verfahren, LDAP, ...
if (PasswortPasst(cUser, cPass)) then begin
oRes.AddPair('status' , 1);
oRes.AddPair('_OBS_JWT_ID' , cUser);
oRes.AddPair('_OBS_JWT_SUBJECT', cUser);
oRes.AddPair('_OBS_JWT_AUDIENCE', 'mandant1');
end else begin
oRes.AddPair('status', 9);
oRes.AddPair('error' , 'Login fehlgeschlagen');
end;
result := oRes.ToJSON();
finally
MyFreeAndNil(oRes);
end;
end;
Rückgabewerte:
| status | Bedeutung |
|---|---|
| 1 | Erfolgreich, der Server erzeugt aus den _OBS_JWT_*-Werten einen Token (HS256) |
| 9 | Misserfolg, der Server antwortet mit 403 und protokolliert den Wert von error |
Fehlerbehandlung im Skript
Tritt im Skript eine Exception auf oder schlägt die Syntax-Prüfung fehl, antwortet der Server mit 500 Interner Fehler und protokolliert die Detail-Meldung in RESTSRV_PROTO (mit Skript-Fehlertext). Der Konsument sieht keine internen Details.
Will das Skript einen spezifischen Fehler an den Konsumenten zurückgeben, ist eine eigene JSON-Struktur zu liefern:
function Post(oParams: TStrings; oBody: TJSONObject): string;
var oRes: TJSONObject;
begin
oRes := TJSONObject.Create();
try
if (Empty(oParams.Values['kundennr'])) then begin
oRes.AddPair('status', 'error');
oRes.AddPair('msg' , 'Parameter ''kundennr'' fehlt');
result := oRes.ToJSON();
exit;
end;
// ...
finally
MyFreeAndNil(oRes);
end;
end;
Skript-Cache
Der Server cached das kompilierte Skript pro Endpunkt. Änderungen am Skript über F7 sind sofort wirksam - der Server erkennt die Änderung am sys_date und compiliert neu.
Verfügbare Bibliotheken
Im Skript können alle OBS-Standard-Bibliotheken verwendet werden. Typische Einstiegspunkte:
- Base.Tools - String-, Datum-, IIF-, Empty-Helper
- Base.DB / Base.xQuery - Datenbank-Operationen, DB_SQLVal, DB_SOpen, DB_LSeek
- Base.qSqlReg - qSqlInit, qSet, SaveData
- System.JSON - JSON-Objekte und -Arrays
- Base.ToolsConst - Konstanten wie CRLF, SINGELQUOTE, EMPTY_DATE
Konkrete Beispiele: