OBS/Kostenpflichtige Module/RESTServer/Scripting: Unterschied zwischen den Versionen

Aus OBS Wiki
Zur Navigation springen Zur Suche springen
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
 
(Eine dazwischenliegende Version desselben Benutzers wird nicht angezeigt)
Zeile 1: Zeile 1:
{{Kostenpflichtige Module}}
{{Kostenpflichtige Module}}


=Anleitung fuer Endpunkt-Skripte=
=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.
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.
Zeile 7: Zeile 7:
==Sicherheitshinweise==
==Sicherheitshinweise==


{{Hinweis|Endpunkt-Skripte verarbeiten Daten aus dem Internet. Uebergabeparameter duerfen niemals ungeprueft in SQL-Anweisungen eingebaut werden. Werte immer ueber ''DB_SQLVal'' bzw. Parameter-Bindings absichern, Eingabewerte gegen Whitelists pruefen.}}
{{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 pruefen (z.B. Pflichtfelder, erlaubte Typen, Wertebereiche).
* Eingaben gegen erwartete Werte prüfen (z.B. Pflichtfelder, erlaubte Typen, Wertebereiche).
* Nur das zurueckgeben, was der Konsument wirklich braucht - keine internen IDs, keine Sys-Felder, keine Passwoerter.
* 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 zurueckgeben; stattdessen schreibt der Server ohnehin Detail-Eintraege in '''RESTSRV_PROTO'''.
* Bei Fehlern keine internen Details an den Konsumenten zurückgeben; stattdessen schreibt der Server ohnehin Detail-Einträge in '''RESTSRV_PROTO'''.


==Methoden-Signatur==
==Methoden-Signatur==
Zeile 23: Zeile 23:
  function Patch (oParams: TStrings; oBody: TJSONObject): string;
  function Patch (oParams: TStrings; oBody: TJSONObject): string;


Nicht implementierte Methoden liefern automatisch ''405''-aehnliche Fehler ueber die generische Skript-Antwort.
Nicht implementierte Methoden liefern automatisch ''405''-ähnliche Fehler über die generische Skript-Antwort.


==Parameter==
==Parameter==
Zeile 29: Zeile 29:
===oParams (TStrings)===
===oParams (TStrings)===


Enthaelt alle Query-Parameter, POST-Parameter (form-urlencoded) sowie die durchgereichten HTTP-Header. Werte sind immer Strings.
Enthält alle Query-Parameter, POST-Parameter (form-urlencoded) sowie die durchgereichten HTTP-Header. Werte sind immer Strings.


Filterung durch den Server:
Filterung durch den Server:


* Geblockte Header werden nicht durchgereicht: ''authorization'', ''cookie'', ''proxy-authorization'', ''x-forwarded-for'', ''x-real-ip'', ''apikey'', ''api_key''.
* Geblockte Header werden nicht durchgereicht: ''authorization'', ''cookie'', ''proxy-authorization'', ''x-forwarded-for'', ''x-real-ip'', ''apikey'', ''api_key''.
* Parameter mit Praefix '''_OBS_''' koennen von aussen nicht gesetzt werden; sie sind fuer interne Werte reserviert.
* 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.
* Werte werden auf max. 1024 Zeichen begrenzt.
* Null-Bytes und Steuerzeichen (ausser Tab, CR, LF) werden entfernt.
* Null-Bytes und Steuerzeichen (ausser Tab, CR, LF) werden entfernt.
Zeile 46: Zeile 46:
===oBody (TJSONObject)===
===oBody (TJSONObject)===


Enthaelt den Request-Body, sofern dieser ein gueltiges JSON-Objekt ist. Bei leerem oder nicht-JSON-Body wird ''nil'' uebergeben.
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
  if (Assigned(oBody)) then begin
     oBody.TryGetValue<string>('email', cEmail);
     oVal := oBody.GetValue('username');
     oBody.TryGetValue<integer>('menge', nMenge);
     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-Groesse: 10 MB.
Maximale Body-Grösse: 10 MB.


===Reservierte _OBS_-Parameter===
===Reservierte _OBS_-Parameter===


Bei aktiver JWT-Authentifizierung stehen die Token-Claims als Parameter zur Verfuegung:
Bei aktiver JWT-Authentifizierung stehen die Token-Claims als Parameter zur Verfügung:


{| class="wikitable"
{| class="wikitable"
Zeile 69: Zeile 77:
|}
|}


Diese Werte werden vom Server gesetzt und koennen vom Skript fuer Berechtigungs- und Mandantenpruefungen verwendet werden.
Diese Werte werden vom Server gesetzt und können vom Skript für Berechtigungs- und Mandantenprüfungen verwendet werden.


==Rueckgabe==
==Rückgabe==


Die Rueckgabe ist immer ein '''JSON-String'''. Wird ein leerer String zurueckgegeben, 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.
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:
Einfaches Beispiel:
Zeile 94: Zeile 102:
==JWT-Authentifizierungs-Skript==
==JWT-Authentifizierungs-Skript==


Bei Zugaengen mit aktiver JWT-Pflicht wird ueber den JWT-Endpunkt das im Zugang hinterlegte Authentifizierungs-Skript aufgerufen (F7 in der Zugaenge-Liste). Das Skript muss eine Methode ''Authenticate'' bereitstellen:
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>
<syntaxhighlight lang="pascal" line>
Zeile 118: Zeile 126:
         end;
         end;


         // Pruefung gegen eigene Tabelle, Hash-Verfahren, LDAP, ...
         // Prüfung gegen eigene Tabelle, Hash-Verfahren, LDAP, ...
         if (PasswortPasst(cUser, cPass)) then begin
         if (PasswortPasst(cUser, cPass)) then begin
             oRes.AddPair('status'          , 1);
             oRes.AddPair('status'          , 1);
Zeile 136: Zeile 144:
</syntaxhighlight>
</syntaxhighlight>


Rueckgabewerte:
Rückgabewerte:


{| class="wikitable"
{| class="wikitable"
Zeile 148: Zeile 156:
==Fehlerbehandlung im Skript==
==Fehlerbehandlung im Skript==


Tritt im Skript eine Exception auf oder schlaegt die Syntax-Pruefung 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.
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 zurueckgeben, ist eine eigene JSON-Struktur zu liefern:
Will das Skript einen spezifischen Fehler an den Konsumenten zurückgeben, ist eine eigene JSON-Struktur zu liefern:


<syntaxhighlight lang="pascal" line>
<syntaxhighlight lang="pascal" line>
Zeile 173: Zeile 181:
==Skript-Cache==
==Skript-Cache==


Der Server cached das kompilierte Skript pro Endpunkt. Aenderungen am Skript ueber F7 sind sofort wirksam - der Server erkennt die Aenderung am ''sys_date'' und compiliert neu.
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.


==Verfuegbare Bibliotheken==
==Verfügbare Bibliotheken==


Im Skript koennen alle OBS-Standard-Bibliotheken verwendet werden. Typische Einstiegspunkte:
Im Skript können alle OBS-Standard-Bibliotheken verwendet werden. Typische Einstiegspunkte:


* ''Base.Tools'' - String-, Datum-, IIF-, Empty-Helper
* ''Base.Tools'' - String-, Datum-, IIF-, Empty-Helper

Aktuelle Version vom 19. Mai 2026, 07:15 Uhr

Kostenpflichtige Module

Internet-Shop
UPS
IMS Professional
SMS
Mehrlager-Verwaltung
Mehrsprachen Modul
Multilanguage Modul
EVA Marketing Tool
Termin-Projekte
Edifact-Schnittstelle
Backup Überwachung Email
OBS Geo Daten
DeliSprint / DPD
Filialen
Cashback
Moebelschnittstelle
Dokumenten Manager
DocuWare-Schnittstelle
OFML-Kalkulation
Versicherungsschaden
Gutschriftsanzeigen
Kameraverwaltung
DataInOut
OpenMasterData / IDS
Sammelpositionen


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

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: