Skip to content
Thomas Hufschmidt edited this page Oct 27, 2017 · 2 revisions

Schnittstellen

Die einzelnen Schnittstellen des ILIAS RESTPlugins lassen sich durch einfache HTTP/1.0 Anfragen aufrufen. Ist das RESTPlugin beispielsweise unter https://api.example.de erreichbar, kann über eine HTTP-GET Abfrage an https://api.example.de/v2/util/routes mittels Browser oder Curl eine Liste aller verfügbarer API-Schnittstellen bzw. Routen abgerufen werden.

Im Normalfall muss diese API-URL (Umleitung) allerdings erst innerhalb des Webservers eingerichtet werden. Ist eine ILIAS-Instanz mit installiertem REST-Plugin unter der url https://example.de/lms/ilias.php erreichbar, so kann die REST-Schnittstelle alternativ auch über die URL https://example.de/lms/Customizing/global/plugins/Services/UIComponent/UserInterfaceHook/REST/RESTController/api.php erreicht werden.

Alle im folgenden beschriebenen REST API-Schnittstellen bzw. Routen benötigen eine Form der Authentifizierung. Das ILIAS RESTPlugin unterstützt im aktuellen Zustand die Authentifizierung mittels OAuth2 (empfohlen) sowie mittels Client-Zertifikaten.

Für eine umfassende Einführung in die wichtigsten REST-Prinzipien ist "RESTful Web Services" von O'Reilly Media zu empfehlen, jedoch keineswegs notwendig. Alternativ lassen sich im Netz viele kurze Einführungen zur Verwendung von REST-Schnittstellen finden, siehe beispielsweisew http://rest.elkstein.org/.

Ein- / Ausgabeverarbeitung

Standardmäßig erzeugt das RESTPlugin Ausgaben im JSON Format, dies kann jedoch durch setzten des Content-Type Request-Headers gesteuert werden. Unterstützt werden aktuell 'application/json' für JSON und 'application/xml' für XML-Ausgabe. Folgender API-Aufruf gibt beispielsweise die Liste verfügbarer Routen im XML-Format zurück:

curl -i -X GET -H 'Content-Type:application/xml' 'https://api.example.de/v2/util/routes'

Analog lässt sich die Verarbeitung von Eingabeparametern steuern. Das ILIAS RESTPlugin ist in der Lage Parameter aus Get/Query-String, Request-Header und Post/Request-Body auszulesen und unterstützt für Post/Request-Body Parameter, anhängig vom Content-Type Request-Header, die Formate 'application/x-www-form-urlencoded', 'application/json' (standard) und 'application/xml' [3].

Intern verarbeitet das ILIAS RESTPlugin Ein- und insbesondere Ausgabeparameter mittels assoziativer PHP-Arrays. Für das JSON-Format besteht dabei ein einfaches 1:1 Mapping zwischen assoziativem PHP Array und JSON-Objekt. Bei der (Ein-) und Ausgabe im XML-Format wird folgendes Mapping angewendet:

  • Jeder Schlüssel eines assoziativen Arrays wird zu einem neuem XML-Element:
    • Jeder Unterstrich im Schlüssel wird zu einem einfachen Strich konvertiert
    • Benutzte den Schlüssel als Tagnamen, falls der Schlüssel einen validen XML-Tag darstellt, zb. nicht numerisch ist.
    • Andernfalls benutze 'item' als Tagnamen und setze ein Attribut 'key' mit dem Schlüssel als Wert.
  • Numerische Arrays werden als assoziative Arrays mit stetigen ganzzahl Schlüsseln behandelt
  • Alle anderen Werte (String, Integers, Floats, Boolean) werden als Inhalt in ihr Elternelelemt eingefügt

Die Eingabe wird entsprechend invertiert verarbeitet, dies bedeutet dass alle Attribute außer 'key' für 'item'-Tags ignoriert werden und das sich numerische Arrays im XML-Format durchs eine Liste von 'item'-Tags mit numerischen 'key'-Attributen darstellen lassen.

Das folgende Beispiel zeigt die Benutzung von Strings, Integers und (numerischen) Arrays:

XML:

<?xml version="1.0" encoding="UTF-8"?>
<payload>
  <!-- string, CDATA is optional but recommended -->
  <login><![CDATA[imogen_reed]]></login>
  <!-- integer -->
  <id>232</id>
  <!-- numeric array -->
  <roles>
    <item key='0'>2</item>
    <item key='1'>285</item>
  </roles>
  <!-- assoziative array -->
  <nested>
    <nest-item-a>Hello</nest-item-a>
    <nest-item-b>World</nest-item-b>
  </nested>
</payload>

JSON:

 {
   "login": "imogen_reed",
   "id": 232,
   "roles": [ 2 285],
   "nested": {
     "nest-item-a": "Hello",
     "nest-item-b": "World"
   }
 }

Fehlerbehandlung

Bei der Verarbeitung kann es immer zur Problemen oder Fehlern kommen, wie zb. falsche Eingabewerte, fehlende Rechte, abgelaufenes Access-Token usw.. Das ILIAS RESTPlugin wird in diesen Fällen terminieren und eine möglichst spezifische Fehlermeldung an die aufrufende Applikation zurück liefern.

Erste Indikator für eine fehlgeschlagene Operation ist ein HTTP Status-Code ungleich 200 (OK). In der Regel enthalten diese Antworten zusätzlich drei Rückgabeparameter um die Fehlerursache genauer zu beschreiben:

  • message: Eine für den Menschen lesbare Fehlermeldung
  • status: Eine für eine Maschinen lesbare Fehlermeldung um mehr Granularität als simple HTTP Status-Codes zu erzielen
  • data: Zusätzliche Daten die eventuell bei der automatischen Verarbeitung helfen können. Der genaue Inhalt hängt vom Typ des Fehlers ab.

Authentifizierung mit oAuth2

Für die Authentifizierung mittels oAuth2 muss bei jedem Aufruf einer geschützten Route ein valides oAuth2 Access-Token im Authorization Request-Header mit Typ Bearer (vgl. Basic für HTTP-Basic-Auth) mitgegeben werden. Ein Access-Token ist immer sowohl an einen oAuth2 API-Key [1] zur Einschränkung der erlaubten API-Schnittstellen als auch an einen ILIAS User-Account für ILIAS RBAC-Kontrollen gebunden.

Access-Tokens sind immer zeitlich begrenzt (einstellbar, standardmäßig 30 Minuten), da sie in sich selbst geschlossen sind um eine Authentifizierung ohne Austausch von Resource-Owner Credentials (Benutzername / Passwort) zu erlauben, so dass nach Ablauf des Access-Token ein neues generiert werden muss.

Um ein zeitlich limitiertes Access-Token zu erzeugen, sind folgende Informationen erforderlich um personalisierten Zugriff für die meisten API-Routen zu erhalten [2]:

  • Einen oAuth2 API-Key der Zugriff auf die gewünschten Schnittstellen hat (Beispiel: 'Haimatsu')
  • Einen ILIAS Benutzer mit ausreichend RBAC-Rechten für die gewünschte Operation und das entsprechende Passwort des Benutzers (Beispiel: 'imogen_reed' / 's0m4')

Mit diesen Informationen lässt sich ein Access-Token (mittels Resource-Owner Password Credentials Workflow) durch eine HTTP-POST Anfrage an https://api.example.de/v2/oauth2/token mit folgenden Parametern erzeugen:

  • grant_type: "password"
  • api_key: "Haimatsu"
  • username: "imogen_reed"
  • password: "s0m4"

Als Curl-Befehl (Parameter im 'x-www-form-urlencoded' Format) kann auf folgende Weise ein Access-Token generiert werden:

curl \
  -i \
  -X POST \
  --data 'grant_type=password&api_key=Haimatsu&username=imogen_reed&password=s0m4' \
  'https://api.example.de/v2/oauth2/token'

Alternativ: Parameter im JSON Format:

{
  "grant_type": "password",
  "api_key": "Haimatsu",
  "username": "imogen_reed",
  "password": "s0m4"
}

Alternativ: Parameter im XML Format:

<?xml version="1.0" encoding="UTF-8"?>
<payload>
  <grant-type>password</grant-type>
  <api-key>Haimatsu</api-key>
  <username>imogen_reed</username>
  <password>s0m4</password>
</payload>

Auf diese Weise wird ein Access-Token erzeugt das man in der Regel für mehreren aufeinanderfolgende API-Anfragen verwenden kann. Bis es schließlich eventuell ausläuft und eine Anfrage mit einem HTTP Status-Code 401 und folgender Rückgabe fehlschlägt:

{
  "message": "Token has expired (Type: Access-Token).",
  "status": "RESTController\\core\\auth\\Base::ID_EXPIRED",
  "data": {
    "type": "Access-Token"
  }
}

In diesem Fall muss ein neues Access-Token erzeugt und die fehlgeschlagene Operation wiederholt werden. Weitere nützliche Informationen zum oAuth2-Standard und seinen Workflows lassen sich im RFC 6749 nachschlagen.


[1] Im oAuth2-RFC als Client-ID bezeichnet. Wegen einer Namenskollision mit ILIAS client_id werden diese im RESTPlugin-Kontext jedoch als API-Key bezeichnet.

[2] Diese Beschreibung bezieht sich auf den personalisierten Zugriff mittels "Resource-Owner Password Credentials Workflow", für reine System-Account bietet sich eher der "Client Credentials Workflow" an.

[2] Parameter die im Request-Body übergeben werden müssen für Content-Type 'application/xml' mit <?xml ...>-Deklaration und genau einem Wurzelelement übergeben werden.