22. API voor Brocade

22.1. Waarom een API?

Door het aanbieden van een gecontroleerde en gestructureerde API kunnen externe applicaties beter samen werken met Brocade. Een paar mogelijkheden zijn:

  • bezitsinformatie naar buiten brengen voor externe opac zoals mijnbibliotheek.be of DiZ
  • reserveringsaanvragen via externe opac

22.2. De techniek achter de API en waarom?

Voor aan het API framework is begonnen, is er gekeken naar verschillende mogelijke implementaties. Qua technologie waren er eigenlijk twee mogelijke kandidaten: PHP en Python.

Uiteindelijk is er gekozen voor PHP en meer bepaald voor het [SLIM] framework te gebruiken als basis. Facts die deze keuze motiveren:

  • kan geïnstalleerd worden m.b.v. Composer
  • volgt nauwgezet de PSR (=PHP standards recommendation) en dus zoveel mogelijk de standaarden gedefinieerd door IETF (=Internet Engineering Task Force)
  • de ontwikkelaars achter dit framework zijn niet van de minste, bijvoorbeeld Josh Lockhart wiens boek [ModernPHP] zeker een aanrader is voor elke PHP-ontwikkelaar.

Daarnaast is er nog communicatie nodig tussen dit framework en Brocade. Nu al de nodige meta-informatie voor het fungeren van de API zit in nieuwe meta. Deze nieuwe meta wordt ook automatisch geëxporteerd naar JSON-files en op deze manier kan het framework aan validatie doen zonder hiervoor Brocade extra te belasten. Het is pas als een aanvraag aan alle opgelegde voorwaarden voldoet dat een request naar Brocade wordt gestuurd. Hier onder wordt de flow en structuur in meer detail uitgelegd.

22.2.1. Structuur en flow van het API framework

De folder-structuur is als volgt:

  • api
  • app: de eigen ontwikkelde software
  • public: de publieke pagina’s, die dus via een URL bereikbaar zijn.
  • index.php: de routing van apicalls en documentatie
  • src: de eigen ontwikkelde PHP-classes
  • templates: PHP-templates, in functie van documentatie
  • tests: PHP-unit tests
  • bootstrap.php: dit is de belangrijkste configuratie file
  • assets: extra bestanden zoals figuren, css en js. Nu staan hier alleen bestanden in functie van documentatie
  • phpdoc: de automatisch gegenereerde documentatie op basis van commentaren in de code
  • share
  • db: local-storage files, nu gebruikt voor nonce-counter bij authenticatie
  • logs: alle soorten logs (beheersbaar via de API toolcat)
  • vendor: alle third-party software, geïnstalleerd via composer
  • composer.json

Volgende figuur visualiseert de flow

_images/flowApi.png

In deze flow zie je dat naast uwww elke apicall ook langs uwwwapi passeert. Hier gebeuren generieke zaken die voor alle apicalls van toepassing zijn:

  • controleren van toegangssloten
  • zetten van UDlg als deze als parameter FDlg doorkomt

22.3. API testen

Om te testen of het API framework succesvol is geïnstalleerd op een server, is de simpelste manier surfen naar getinfo.

Waarschuwing

Dit wil nog niet zeggen dat de API actief is en gebruikt kan worden. Hiervoor moet ten eerste de API op actief gezet worden en moeten er apicalls ter beschikking worden gesteld (zie verder).

Nu als de API volledig up-and-running is kun je deze het makkelijkste testen met SoapUI.

22.4. Release informatie

Zoals eerder vermeld heeft het API framework composer nodig om te installeren:

~> mkdir /tmp/composer
~> cd /tmp/composer
~> curl -sS https://getcomposer.org/installer | php
~> mv composer.phar /usr/local/bin/composer
~> cd ..
~> rm -rf /tmp/composer

Alternatief is van een bestaande API de vendor folder te kopiëren.

Daarnaast zijn ook de volgende delphi-waarden nodig (worden aangemaakt in release.py)

  • api-active: of de API actief is(=1) of niet(=0) standaard best API of inactive zetten, je kunt deze met toolcat api -start activeren. Hierop wordt gecontroleerd in Call.php
  • api-db-dir: de directory waar database-files worden op geslaan Deze directory moet schrijfrechten hebben.
  • api-dbmethod: de methode om databank op te slaan, mogelijke waarden zijn ‘PDO’ en’JSON’. PDO maakt onderliggende gebruikt van SQLite.
  • api-log-dir: de directory waar logs naar moeten Deze directory moet schrijfrechten hebben.
  • api-mode: bepaalt of de API in productie is (=production) of ontwikkeling/test (=development)
  • api-calls-allowed-without-auth: calls die ook zonder authenticatie worden opgeroepen, gescheiden door een komma
  • api-default-user-without-auth: als een call (legaal) wordt opgeroepen zonder authenticatie, welke user dan gebruiken?
  • m-api-exe: de routine die opgeroepen wordt (op termijn niet enkel voor deze web service nodig, dus moet vaste waarde worden bij installatie)

Waarschuwing

Als je een delphi-waarde aanpast, moet je ook api -restart uitvoeren zodanig dat deze nieuwe waarden ook in de code worden gebruikt.

22.5. API aanroepen via HTTP(S)

Metadata wordt meegegeven via de header data, momenteel wordt volgende header-data geparsed

HTTP_AUTHORIZATION
Nodig om de gebruiker the authenticeren (zie Authenticatie)
HTTP_ACCEPT

In welk formaat je het resultaat wilt. Bijvoorbeeld header("Accept: application/json;q=1,application/xml;q=0.6,*/*;q=0.5"). Momenteel worden volgende outputs ondersteund:

  • application/json (default waarde)
  • application/xml
  • text/html

22.5.1. Authenticatie

Vooreerst moet je om de API te gebruiken je authentificeren. Hiervoor moet je een Authorization meegeven in de header-data. Even nog het onderscheid tussen authenticatie en autorisatie (de header-naam Authorization maakt het verwarrend):

Authenticatie
Verificatiemethode om de identiteit van een gebruiker te controleren, bijvoorbeeld door een combinatie gebruikersnaam en wachtwoord.
autorisatie
De rechten om bepaalde handelingen uit te voeren, bijvoorbeeld e-mail adres van persoon X opvragen en/of wijzigen

Voor de Brocade API wordt de autorisatie volledig in Brocade afgehandeld d.m.v. onder andere de gekoppelde Brocade gebruiker bij de API gebruiker en toegangssloten.

Hier onder worden de mogelijke authenticatie-schema’s opgesomd met mogelijke nadelen. Nu, als het kanaal kan worden vertrouwd, dan is het onderliggende authenticatie mechanisme niet iets om ongerust over te zijn. Het uitgangspunt is ook om de API enkel toe te laten via SSL. De eventuele nadelen van SSL wegen niet op t.o.v. de voordelen. Kijk bijvoorbeeld naar Why HTTP Is Sometimes Better Than HTTPS en let vooral op de dateails :-)

Onder Beheer van API gebruikers [link] worden volgende gegevens gedefinieerd relevant voor authenticatie:

Identifier
De naam/id van de API gebruiker.
Actief?
Of de account actief is of niet.
Api-key
De API key, wordt binnen de context van authenticatie gebruikt als wachtwoord.

Om makkelijker geldige authorization-headers te maken kun je gebruik maken van de toolcat toepassing api -genhttpauth.

Met Soap-UI kun je rechstreeks username en key ingeven:

_images/soapuiAuth.png

22.5.1.1. HTTP’s basic access authentication

[rfc2617]

Dit is de simpelste vorm van authentificatie via HTTP(S). De API gebruiker zet gewoon de volgende header: Authorization: Basic X Met als waarde voor X een base64 geëncrypteerd string van een vorm als base64encode(identifier+":"+apikey). Als deze string overeenkomt met credentials van de gebruiker wordt deze geauthentificeerd en krijgt deze het gewenste resultaat. Als de call niet over SSL gaat, heeft deze methode meerdere (onaanvaardbare) nadelen:

  • Aanvallers kunnen de API key van een gebruiker achterhalen
  • De server moet de API key in plain text opslaan
  • Replay: als de call onderschept wordt, can de call in toekomst herhaald worden
  • Reflection attack: aanvallers kunnen een server faken, autorisatie bemachtigen en zich daarna voordoen als de API gebruiker in kwestie
  • Man-in-the-middle: Aanvallen kan zich voor doen als API gebruiker en zelfs de requests aanpassen.

22.5.1.2. HTTP Digest access authentication

[rfc2617]

Bij dit authenticatie algoritme via HTTP(S) moet als parameter zeker realm gedefinieerd zijn en wordt een request van volgende vorm verwacht:

HA1 = MD5(A1) = MD5(identifier:realm:apikey)
HA2 = MD5(A2) = MD5(method:URI)
Authorization: digest username="..", response=MD5(HA1:nonce:HA2)

Waarbij volgende extra parameters kunnen worden meegestuurd:

  • algorithm: default-waarde is MD5, maar kan aangepast worden door elk algoritme gekend door API framework, zie getinfo.

Om even een concreet voorbeeld te nemen, stel:

  • Identifier=bibnet
  • Api-key=u5g3e73pg87erzfcdp33
  • nonce=30034479
  • realm=dev.anet.be
  • call=dev.anet.be/brocade/api/getholdingsall

Waarbij de eerste 2 parameters gedefinieerd staan bij de API gebruiker in Brocade en de voorlaatste een parameter is van het authenticatie algoritme.

Dan is dit een geldige authenticatie

Authorization:digest username="bibnet",
       algorithm="sha512", nonce="30034479",
       response="af2032ac6420547ad2a5e5d7f1fb3e4adeb018d24d6aa739aef5bf4570a38e6799efca9e226a27c98153c3e3b94262c6816b8f3284f89899c8750a4ab587917e"

Nu om deze methode veilig te houden voor Replay en Reflection attack wordt aangeraden om nonce maar 1 keer te gebruiken. Dit laatste wordt echter momenteel nog niet afgedwongen.

Er zijn 2 nadelen aan deze methode

  • Man-in-the-middle attack (indien niet over SSL)
  • API gebruiker kan geen aanvragen pipen.

Een oplossing hiervoor is QoP gebruiken.

22.5.1.2.1. quality of protection (qop)

Qop is een uitbreiding op HTTP Digest access authentication, met meerdere versies. Een mogelijke header kan zijn:

Authorization: digest username="bibnet",
     algorithm="md5",
     nonce="30034479",
     cnonce="123456",
     qop="auth",
     nc="0001",
     response="1ae9a4c32f402230292975397e8b4603"

Als qop=auth of auth-int, is response=MD5(HA1:nonce:nc:cnonce:qop:HA2). Maar met bovenstaande call heb je dus alle data om response zelf te berekenen en dus te authentificeren. Voordeel is dat in dit geval Bibnet zelf verantwoordelijk is om zijn cnonce aan te passen om response uniek te houden. nc dient om bij te houden de hoeveelste keer eenzelfde cnonce gebruikt wordt. Dus dit moet dan wel intern worden bijgehouden. Voordeel is dat je zo samen horende calls kan bundelen. Bijvoorbeeld om het in macro’s uit te drukken, call naar m4_calcDemandLibs gevolgd door call naar m4_setDemand.

Nu naargelang waarde van qop wordt ook HA2 anders berekent. Indien qop=auth dan HA2=MD5(method:URI). Indien qop=auth-int dan HA2=MD5(method:URI:MD5(entityBody)), dus als het ware de waarde van de parameters tellen ook mee. Belangrijk is dan natuurlijk om een volgorde op te geven voor de parameters. Typisch alfabetische volgens van de key-waarden.

Voor bepalen van HA1 word ook gekeken naar algorithm. Indien algorithm eindigt op -sess dan is HA1=MD5(MD5(identifier:realm:apikey):nonce:cnonce), anders is HA1=MD5(identifier:realm:apikey).

Stel dat volgende parameters worden meegestuurd cloi=WyJjOmx2ZDoxMDAyNzYiXQ&debug=0, dan is het volgende met bovenstaande gegevens een geldige authenticatie

Authorization: digest username="bibnet",
   algorithm="md5-sess",
   nonce="30034479",
   cnonce="123456",
   qop="auth",
   nc="0001",
   response="71c5c1ebc2f800215dfb81f10a9f974a"

22.5.2. Authenticatie van eindgebruikers

Of een API gebruiker calls kan doen, en hoe, die betrekking hebben op een eindgebruiker wordt bepaald in Beheer van API gebruikers [link] onder Validatie van eindgebruikers.

Bepaal of en hoe een API gebruiker calls mag oproepen die betrekking hebben op persoonlijke data van eindgebruikers aan de hand van de volgende parameters:

  • Trusted party: Vink dit aan als deze API gebruiker een betrouwbare partij is en dus niet eerst de credentials van een eindgebruiker moet doorgeven.
  • Geldigheid validatie: Geef in minuten, de tijd dat een validatie van eindgebruiker geldig is na het valideren van een eindgebruiker m.b.v. mt:apicall:validatepatronlogin.
  • Rechten: Geef een statement dat RDrights bepaald. Als RDrights=R dan heeft de API gebruiker alleen maar rechten om gegevens van de eindgebruiker op te vragen. Indien RDrights=W dan mag deze ook acties in naam van de eindgebruiker uitvoeren zoals het plaatsen van een reservatie.

22.5.2.1. Valideren eindgebruiker

Valideren van een eindgebruiker gebeurt via de call validatepatronlogin.

Kijk of de id, wachtwoord en sentinel van een eindgebruiker valid zijn.

  • id: eindgebruikers identificatie. Dit zijn alle velden die een eindgebruiker kunnen duiden: userid, barcode, eloi, email-adres, administratief nummer, ...
  • password: deze waarde is de MD5 berekend op de concatenatie van $sentinel (vooraan) en het wachtwoord (achteraan) van de gebruiker. (als de sentinel leeg is, is dit het wachtwoord zelf)
  • sentinel: Dit is een willekeurige string. De lengte van de sentinel moet langer dan 32 karakters zijn. Brocade garandeert dat een sentinel slechts 1x per dag kan worden gebruikt.

Zo ja, geef de eloi, persoonsgegevens en geldigheidsduur terug.

Om tijdens het testen makkelijk de nodige parameters mee te geven aan validatepatronlogin kun je gebruik maken van de toolcat api -genpatronvalidation, bijvoorbeeld:

~> api -genpatronvalidation eloi=e:UA:185629
id=grobijns
sentinel=1455116279.***************
password=02a35f7e77*******************
decrypt=eyJhbGciOiAiYmFzZTY0IiwgImVuYyI6IC**********

Je kan de parameter decrypt (zie decrypt) dan integraal kopieren en een de call als volgt doen /brocade/api/validatepatronlogin?decrypt=eyJhbGciOiAiYmFzZTY0IiwgImVuYyI6IC**********. Het resultaat is dan bijvoorbeeld van de vorm:

{
 "info":    {
    "Apiuser": "bmoelans",
    "IP": "212.88.229.242",
    "UDapicall": "validatepatronlogin",
    "UDapises": "",
    "UDdesk": "brocade",
    "UDuser": "bmoelans",
    "time": "24.05.2016 13:55:45"
 },
 "status": 200,
 "data":    {
    "address":       [
                {
          "boxnumber": "",
          "city": "Paal",
          "country": "BE",
          "nr": "83",
          "phone": "",
          "street": "Industrieweg",
          "type": "werk",
          "zip": "3583"
       },
                {
          "boxnumber": "",
          "city": "De Haan",
          "country": "BE",
          "nr": "12",
          "phone": "",
          "street": "Genebroekstraat",
          "type": "thuis",
          "zip": "B-8420"
       },
                {
          "boxnumber": "",
          "city": "Hoogstraten",
          "country": "BE",
          "nr": "9",
          "phone": "",
          "street": "Boxtelstraat",
          "type": "thuis",
          "zip": "2320"
       }
    ],
    "eloi": "e:UA:185629",
    "email": "greet.robijns@ciblis.be",
    "email_validated": "Y",
    "firstname": "Greet",
    "gsm": "0494164071",
    "homelib": "UA-CST",
    "id": "e:UA:185629",
    "lastname": "Robijns",
    "message": "",
    "sex": "F",
    "validuntill": "24.05.2016 14:55:45"
 },
 "success": "true"
 }

22.6. Implementatie in Brocade

Hieronder worden de schermen om de API te parametriseren in Brocade beschreven.

22.6.1. apiuser

Beheer van API gebruikers [link]

Om een API call te doen moet er een identificatie gebeuren van de gebruiker (BiBnet, VuFind, mobile App,...). Hoe een API gebruiker zich moet identificeren wordt in dit scherm gedefinieerd. Er zijn momenteel slechts enkele mogelijkheden die op termijn kunnen uitgebreid worden.

Verder koppel je in dit scherm de API gebruiker met een Brocade gebruiker want de permissies die een API gebruiker heeft worden bepaald aan de hand van de Brocade gebruiker. Bij het invullen van de Brocade gebruiker:

  • Maak best een aparte Brocade gebruiker aan die je gebruikt voor API doeleinden voor een bepaald leensysteem. Het is geen goed idee om een algemene Brocade gebruiker te nemen
  • De Brocade gebruiker moet actief zijn en voldoen aan de vereiste wachtwoordpolicy net zoals andere gebruikers. Met dat verschil dat normaal dit wachtwoord niet gecommuniceerd wordt aan de API gebruiker.
  • Geef deze gebruiker alleen de noodzakelijk permissies.
Identificatie

In deze blok koppel je de API gebruiker met een Brocade gebruiker mits de voorwaarden binnen dit blok zijn voldaan.

Brocade gebruiker [user: tekst]
Koppel hier de API gebruiker met een Brocade gebruiker om zo beperkingen op te leggen die een Brocade gebruiker ook kan hebben (standaard taal, toegelaten eindgebruikerssystemen,...)
Actief? [active: boolese waarde]
Vink aan als deze API user actief is. Zo nee wordt alles van deze API gebruiker geblokkeerd
Api-key [key: tekst]
Een unieke key om een API gebruiker te authentificeren
Werkstation [workstation: tekst]
Geef het werkstation waaronder acties van deze API gebruiker moeten gelinkt worden.
Toegelaten IP-gebieden [ipallowed: herhaalbaar, tekst]
Geef hier de IP-ranges die toegelaten zijn. Alleen indien het ip van de caller in deze lijst voorkomt, wordt de call toegelaten. Voor het definiëren van IP-adressen kun je gebruikmaken van een range bijvoorbeeld 143.169.[0-255].[0-255]. Elke range moeten op een nieuwe regel staan. Als je alle ranges wil toelaten vul je [0-255].[0-255].[0-255].[0-255] in.
Gebruikerspatroon [throttle: herhaalbaar, tekst]

Geef hier het aantal calls per tijdsperiode in die deze gebruiker mag doen. Alleen indien het maximum hier aangegeven niet overschreden is, wordt de call toegelaten. Voor het definiëren van de gebruikerspatronen kun je gebruikmaken van een tijd-range UU:MM-UU:MM met daarbij een maximum aantal calls: ‘maximum_/_tijdsperiode’. Elke range moeten op een nieuwe regel staan. Voorbeelden:

  • 00:00-06:00.10/s : Voor 6 uur 10 per seconde
  • 06:00-17:00.10/h : Tussen 6 en 17 uur, 10 per uur
Validatie van eindgebruikers

Bepaal of en hoe een API gebruiker calls mag oproepen die betrekking hebben op persoonlijke data van eindgebruikers aan de hand van de volgende parameters:

  • Trusted party: Vink dit aan als deze API gebruiker een betrouwbare partij is en dus niet eerst de credentials van een eindgebruiker moet doorgeven.
  • Geldigheid validatie: Geef in minuten, de tijd dat een validatie van eindgebruiker geldig is na het valideren van een eindgebruiker m.b.v. mt:apicall:validatepatronlogin.
  • Rechten: Geef een statement dat RDrights bepaald. Als RDrights=R dan heeft de API gebruiker alleen maar rechten om gegevens van de eindgebruiker op te vragen. Indien RDrights=W dan mag deze ook acties in naam van de eindgebruiker uitvoeren zoals het plaatsen van een reservatie.
Trusted party [trusted: boolese waarde]
Vink dit aan als deze API gebruiker een betrouwbare partij is en er dus geen extra controle nodig is of eloi gevalideerd is en nog geldig validatie heeft.
Geldigheid validatie [validationvalid: geheel getal]
Geef in minuten, de tijd dat een validatie van eindgebruiker geldig is.
Rechten [rights: tekst]

Geef een statement dat RDrights bepaald. Bijvoorbeeld:

  • s RDrights="R"

22.6.2. apiparam

Beheer API parameters [link]

Mogelijke parameters bij een apicall en de restricties.

Probeer de parameter zo nauwkeurig mogelijk te specificeren. De gegevens hier onder worden gebruikt om de correctheid van een parameter na te gaan. Pas als een parameter voldoet aan de eisen hier onder opgesomd, wordt de externe call doorgelaten.

Type [type: keuze]

Geef de natuur van de parameter, dit wordt gebruikt om de correctheid van een parameter bij een call te controleren. Mogelijke types zijn:

  • String: alles is een string, dus dit is het minst restrictieve type. Er worden wel enkel ASCII-strings aanvaard.
  • Int: een natuurlijk getal
  • Float: een decimaal getal waarbij . gebruikt wordt als decimaalteken.
  • Meta: een string die een identifier van meta voorstelt, best in combinatie met Meta Type gebruiken.
  • Tijdstip: een combinatie van datum en/of tijd die voldoet aan de ISO 8601 standaard. Deze wordt door PHP geparsed en dan als $h naar Mumps gestuurd.
  • JSON: JavaScript Object Notation

Keuze uit:

  • String [string]
  • Int [int]
  • Float [float]
  • Meta [meta]
  • Datum+Tijd [datetime]
  • JSON [json]
Standaardwaarde [default: tekst]
Voor een niet verplichte parameter kun je hier een standaard waarde opgeven. Deze waarde wordt pas toegepast als deze niet gedefinieerd is in de oproep.
Indien dit veld leeg is en deze parameter wordt niet meegegeven met de call, krijgt deze parameter als waarde de lege string.
Patroon [pattern: tekst]

Geef een reguliere expressie volgens de PCRE standaard waar de input moet aan voldoen. Hierbij alvast enkele voorbeelden:

  • ^e:.+:\\d+$ = een geldige e:loi
  • ^[a-zA-Z].{2,} = een string die met een letter begint en minstens uit 3 karakters bestaat
Meta type [metatype: tekst]
Als de parameter van het type meta is, kun je hier aangeven van welk type deze meta is. Om zo te kunnen controleren of de parameter een bestaande instantie is van de meta. Bijvoorbeeld: workstation of rasys.

22.6.2.1. Speciale parameters

Volgende parameters worden opgevangen in PHP. Ze staan wel als concretisering vermeld maar wijzigingen hieraan in Brocade zullen geen impact hebben op hun werking. Deze parameters kunnen dan ook aan eenderwelke apicall worden meegegeven.

22.6.2.1.1. debug

Deze parameter kun je bij elke call gebruiken zonder deze expliciet te vermelden. Volgende waarden zijn mogelijk

  • 0: geen impact
  • 1: Mumps wordt niet uitgevoerd maar data die naar Mumps zou gaan wordt getoond
  • 2: Er wordt debug-info uit Mumps getoond.
22.6.2.1.2. validateoutput

Deze parameter kun je bij elke call gebruiken zonder deze expliciet te vermelden. Volgende waarden zijn mogelijk

  • 0: geen impact
  • 1: Mumps wordt niet uitgevoerd maar data die naar Mumps zou gaan wordt getoond
  • 2: Er wordt debug-info uit Mumps getoond.
22.6.2.1.3. decrypt

Als deze parameter gezet is weet de parser dat dit het enige argument is en dat de gedecodeerde base64url waarde een JSON string is van de vorm {“alg”:”[encoding]”,”enc”:”[geëncodeerde JSON string met parameters volgens opgeven algoritme]”}

Concreet kun je de parameters van een call encrypteren als volgt:

  1. Steek de parameters in een JSON string met als key en value respectievelijk de parameternamen en -waarden, bijvoorbeeld {"eloi": "e:ua:1234356","lg":"E"}
  2. Encrypteer deze JSON-string met een door Brocade gekend decryptie-algoritme. Ondersteunde algoritmen kun je opvragen via apicall getinfo
  3. Steek deze geëncrypteerde string opnieuw in een JSON-string met als parameters:
    • alg: het decryptie algoritme
    • enc: de geëncodeerde string Stel dat we base64 gebruiken als encodering dat hebben we na deze stap een JSON-string van de vorm: {"alg":"base64","enc":"eyJlbG9pIjogImU6dWE6MTIzNDM1NiIsImxnIjoiRSJ9"}.
  4. Encodeer deze JSON-string met base64 en ken deze toe aan de parameter decrypt. De uiteindelijke call is dan van de vorm ?decrypt=eyJhbGciOiJiYXNlNjQiLCJlbmMiOiJleUpsYkc5cElqb2dJbVU2ZFdFNk1USXpORE0xTmlJc0lteG5Jam9pUlNKOSJ9.
22.6.2.1.4. encrypt

Notitie

[Nog te implementeren] Idee is aan de optionele parameter encrypt een base64 geëncodeerd JOSE(=JSON Object Signing and Encryption) mee te geven. Indien deze wordt meegegeven, wordt de respons met de meegegeven info geëncodeerd.

22.6.3. apicall

Beheer van API calls [link]

Hier definieer je de mogelijke apicalls met hun acties in Mumps. Let wel op dat de identifier case-sensitive is, dus de gebruiker van de API zal exact dezelfde identifier moeten gebruiken in zijn call. Daarom dat de voorkeur gaat naar naamgeving volledig in kleine letters. Verder om de leesbaarheid te vergroten de naamgeving als volgt [action][context][detail].

  • [actie]: get, set, cancel of update
  • [context]: welke context je aanspreekt, bijvoorbeeld ‘holdings’, ‘enduser’, ‘catlib’
  • [detail]: (optioneel) meer detail over welk deel van de data

Voorbeelden zijn: getpatronprofile, updatepatronemail, getholdings

Algemeen

Algemene instellingen

Toon documentatie? [showdoc: boolese waarde]
Vink aan indien de documentatie voor deze apicall publiek mag staan.
Api rubriek [catagory: tekst]
Geef hier aan onder welke categorie de api-call moet komen.
Input

Definieer de vorm van de mogelijke aanroepen.

Parameter [param: herhaalbaar, meerdere velden]

Definieer een mogelijke parameter van de call en geeft aan of deze:

  • optioneel of verplicht is
  • enkelvoudig is of meervoudig (=lijst van waarden)

Velden:

  • Identificatie [metaApicall.paramid]
  • Verplicht [metaApicall.paramreq]
  • Lijst? [metaApicall.parammulti]
Aanroep

Wat gebeurt er als de call geldig is.

Routine [routine: tekst]
De mumps routine die moet worden opgeroepen. Deze krijgt automatisch bovenstaande parameters doorgestuurd als form-inputs. Dus bijvoorbeeld eloi wordt FDeloi.
Audit [audit: tekst]
Naar waar audits wegschrijven, indien leeg worden geen audits weggeschreven
Tijdslot [timeslot: tekst]

Geef een of meerdere tijdsintervallen waarop deze call mag gebeuren. Indien leeg is er geen beperking. De intervallen zijn van de vorm UU:MM-UU:MM of UU:MM-UU:MM dw. Hierbij stelt dw een dag in de week voor. Scheid ze door een komma.

Voorbeelden:

  • 7-12:30,13-14 su
  • 12-14 mo,12-14 tu,12-13 we,12-17 th,12-18 fr,12-19 sa
Toegangssloten [acckey: herhaalbaar, tekst]
Geef de toegangssloten die van toepassing moeten zijn op deze apicall. Dus user van deze call moet toegang hebben tot al deze toegangssloten voordat hij deze call kan doen.
Output

Specificaties m.b.t. de output

Validatie schema [validationschema: tekst]
Geef hier het JSON-schema waaraan de output moet voldoen. Door in de apicall de parameter validateoutput=1 te gebruiken zal onder de key valid ook gemeld worden of de response voldoet aan dit schema. Deze validatie heeft geen invloed op de rest van de response.

22.6.4. apiauth

Beheer van authenticatie schema’s voor API [link]

Authenticatie schema’s voor API

Algemeen

Hier geef je algemene info

Actief [active: boolese waarde]
Vink aan indien deze authenticatie methode gebruikt mag worden op dit systeem
Parameters

Parameters specifiek voor deze authenticatie. De nodige parameters worden best ook opgesomt in de scope note.

Parameter [param: herhaalbaar, meerdere velden]

Geef key en waarde van parameter

Velden:

  • Identifier [metaApiauth.paramkey]
  • Waarde [metaApiauth.paramvalue]

22.6.5. apirub

Elke api-call behoort tot een bepaalde rubriek. Ze worden dan in de documentatie opgedeeld volgens die rubrieken en moeten ook bij het oproepen, de rubriek in hun pad bevatten.

Beheer van API rubrieken [link]

Definieer hier een categorie waaronder Api-calls kunnen behoren.

Pad in ontwikkelomgeving [brocadepath: tekst]
Geef hier het pad in qtech in, waar de software voor deze calls zich bevinden

Voorbeelden van rubrieken:

  • acquisition
  • api
  • cataloguing
  • demand
  • lib
  • list
  • loan
  • patron
  • statistics

22.7. API uitbreiden

22.7.1. Beschikbare parameters

FD.../FA..
Alle parameters die gedefinieerd zijn onder Beheer van API calls [link] komen in Mumps binnen voorafgegaan door FD voor enkelvoudige parameters of FA voor lijsten.
UAacckey
Alle toegangssloten die van toepassing zijn
UDapi
Heeft altijd waarde=”1”, zodat je in Mumps a.d.h.v. deze variabele kan testen op de call van de API komt of niet.
UDapises
Indien ingevuld verwijst dit naar een sessie nummer van de API gebruiker. Op deze manier kan 1 API gebruiker toch meerdere sessies hebben of expliciet vragen om onder dezelfde sessie te werken. UDapises wordt in Brocade gelinkt met een Brocade-sessie maar bevat dus niet dezelfde waarde.
UDapicall
De gemaakt call (Beheer van API calls [link])
UDapirou
de routine die opgeroepen moet worden
UDapiuser
De ID van de API gebruiker die de call doet, gedefinieerd onder Beheer van API gebruikers [link]
UDaudit
Indien niet leeg, daar waar audits naar geschreven moeten worden.
UDdebug
Als je via de apicall parameter debug=2 zet, wordt UDdebug=1 doorgegeven, anders is UDdebug=0. Ter info je kunt ook debug=1 gebruiken in de call, dan krijg je debug output van het PHP-framework, maar wordt mapi niet opgeroepen.
UDip
IP-adres van waar de call komt
UDrou
Zal altijd de waarde %Entry^uwwwapi hebben indien call vanuit API
UDuser
De gekoppelde brocade gebruiker onder Beheer van API gebruikers [link] voor de API gebruiker
UDwks
Het gekoppelde werkstation voor de API gebruiker

22.7.2. Instructies voor Mumps

Vaak zal datgene wat de API naar buiten brengt, overeenkomen met de data van een bestaand scherm in Brocade (Desktop). Nu spreekt het voor zich dat we zo weinig mogelijk dubbele code willen. Voorstel is om de code die specifiek de API output verzorgt in Mumps, trouwens altijd JSON-formaat, indien mogelijk dan ook in hetzelfde m-bestand zit als het m-bestand waar de code opgeroepen wordt voor het bestaande scherm. Omgekeerde als je API code implementeert waar nog geen ‘grafische’ tegenhanger voor zou zijn, maak de code zodat dit achteraf alsnog kan.

Nu het resultaat gaat (M)JSON zijn en je moet goed nadenken over de vorm. Stel je hebt volgende code

GT.M>s RAout("c:loi:1","vol 1","o:loi:1")=""

GT.M>s RAout("c:loi:1","vol 1","o:loi:2")=""

GT.M>s RAout("c:loi:1","vol 2","o:loi:3")=""

GT.M>m4_dumpMJSON(json,RAout)

Dan krijg je een JSON-string van de vorm {"c:loi:1":{"vol 1":{"o:loi:1":"","o:loi:2":""},"vol 2":{"o:loi:3":""}}}. Maar bijvoorbeeld in PHP komt deze string dan ook binnen in deze JSON vorm en krijg je iets van de vorm:

<?php
$json='{"c:loi:1":{"vol 1":{"o:loi:1":"","o:loi:2":""},"vol 2":{"o:loi:3":""}}}';
$out=json_decode($json,1);
echo "<pre>".var_dump($out)."</pre>";
==
array(1) {
 ["c:loi:1"]=> array(2) {
   ["vol 1"]=> array(2) {
     ["o:loi:1"]=> string(0) ""
     ["o:loi:2"]=> string(0) ""
   }
   ["vol 2"]=> array(1) {
     ["o:loi:3"]=> string(0) ""
   }
 }
}

Wat moeilijk handelbaar is, want al de key-values zijn variabel, dus is het moeilijk om leesbare code te schrijven voor: “loop over alle volumes/olois”. Als we nu in Mumps het volgende doen:

GT.M>s RAout("record","c:loi:1","vol","vol 1","obj","o:loi:1")=""

GT.M>s RAout("record","c:loi:1","vol","vol 1","obj","o:loi:2")=""

GT.M>s RAout("record","c:loi:1","vol","vol 2","obj","o:loi:3")=""

GT.M>m4_dumpMJSON(json,RAout)

Kun je PHP code als volgt schrijven:

<?php
$json='{"record":{"c:loi:1":{"vol":{"vol 1":{"obj":{"o:loi:1":"","o:loi:2":""}},"vol 2":{"obj":{"o:loi:3":""}}}}}}';
$out=json_decode($json);
foreach($out->record as $rec=>$recdate){
  echo "record $rec<br>";
  foreach($recdate->vol as $vol=>$objects){
    echo "|- volume $vol<br>";
    foreach($objects->obj as $obj=>$extradata){
      echo " |+ object $obj<br>";
    }
  }
}

Wat de code meteen semantisch leesbaarder maakt. Bovenstaande code werkt dan ook voor volgende output uit Mumps:

GT.M>s RAout("record","c:loi:1","vol","vol 1","obj","o:loi:1")=""

GT.M>s RAout("record","c:loi:1","vol","vol 1","obj","o:loi:1","bc")="12341"

GT.M>s RAout("record","c:loi:1","vol","vol 1","obj","o:loi:2")=""

GT.M>s RAout("record","c:loi:1","vol","vol 1","obj","o:loi:2","bc")="12345"

GT.M>s RAout("record","c:loi:1","titel")="Leeuw van Vlaanderen"

GT.M>s RAout("record","c:loi:1","vol","vol 2","obj","o:loi:3")=""

GT.M>m4_dumpMJSON(json,RAout)

Echter, moet je hier natuurlijk voorzichtig mee omgaan, niet elke API gebruiker zal het appreciëren als er opeens extra data meekomt. Dus net zoals je in een x-file beslist welke data nu te zien is door de (eind)gebruiker moet je zo een controle hebben voor de JSON-uitvoer. Dus het mag zeker niet zo zijn als je extra data verzameld om de lay-out van een scherm te tweaken, deze automatisch meekomt met de gekoppelde API uitvoer. Dus stel dat voor zowel de HTML- als JSON-representatie een functie gather wordt opgeroepen die een array teruggeeft met alle nodige data, ook al zou deze array perfect kunnen dienen voor de JSON-representatie, dan nog moet er een mapping gebeuren om problemen in de toekomst te vermijden.

Ook zouden alle apicalls min of meer dezelfde logica van vormgeving moeten hebben, zodat het voor de gebruiker ook makkelijk bruikbaar is.

Voorbeeld van mogelijke output voor alle reservaties en reserveringsaanvragen van één eindgebruiker

{
   "info": {
       "Apiuser": "testuser",
       "IP": "212.88.229.242",
       "time": "29.10.2015 11:28:32"
   },
   "data": {
       "enduser": {
           "e:UA:185629": {
               "demand": {
                   "ra:AA:335": {
                       "catobj": {
                           "c:lvd:249011": {
                               "pickuploc": "",
                               "statuscode": "0",
                               "statusmsg": "Being processed",
                               "title": "Tavi"
                           }
                       },
                       "time": "22.10.2015 14:44:36"
                   }
               },
               "hold": {
                   "r:UA:32979": {
                       "object": {
                           "o:lvd:1462066": {
                               "pickuploc": "UA-CST",
                               "statuscode": "1",
                               "statusmsg": "Available",
                               "title": "Images of the world: the atlas through history"
                           }
                       },
                       "time": "29.10.2015 11:00:00"
                   },
                   "r:UA:32980": {
                       "object": {
                           "o:lvd:1239412": {
                               "pickuploc": "UA-CST",
                               "statuscode": "1",
                               "statusmsg": "Available",
                               "title": "Verklarend handwoordenboek der Nederlandsche taal (tevens vreemde-woordentolk)"
                           }
                       },
                       "time": "29.10.2015 11:09:07"
                   }
               }
           }
       }
   },
   "status": 200
}

22.7.3. Voorziene macro’s

macro showAPIresult($result, $format="putJSON"):
    '''
    $editfile: /api/application/api.d
    $synopsis:  Gegeven een mumps array met de naar buiten te brengen gegevens, zet deze om naar een JSON-formaat geschikt voor de API.
    $result: de mumps array
    $format: het formaat waarin $result geparsed wordt. Momenteel werken alleen de volgende voorgedefinieerde  formats:
               - openJSON = open een Json-blok
               - startJSON = openJSON + aanvullen met extra meta-informatie voor API. Typisch gebruikt voor een for-loop
               - addJSON = print JSON-data, ervan uitgaand dat al een deel van de JSON is uitgeschreven. Typisch gebruikt in for-loop
               - closeJSON = sluit een JSON-blok
               - stopJSON = clodeJSON, maar zou op termijn extra info kunnen bevatten. Semantische gezien zou dit allerlaatste statement moeten zijn
               - putJSON = startJSON+addJSON+stopJSON

             Idee is dat je ook zelf gemaakte formats in x-file kunt meegeven
    $example: m4_showAPIresult(RAresult)
    '''

    ««d %addPiJS^bapi($format,.$result)»»
macro showAPIerror($code, $msg):
    '''
    $editfile: /api/application/api.d
    $synopsis:  Genereer een JSON error-melding voor de API
    $code: HTTP status codes, bijvoorbeeld 400 voor een bad request
    $msg: extra boodschap
    $example: m4_showAPIerror(404,"Record niet gevonden")
    '''

    ««s MAer("status")=$code s MAer("msg")=$msg m4_dumpMJSON(MDjson,MAer) w MDjson»»
macro setAPIeuserAuthenticated($apiuser, $apises="", $eloi, $ctime=$h):
    '''
    $editfile: /api/application/api.d
    $synopsis:  Zet dan een API gebruiker rechten heeft om gegevens te lezen en/of wijzigen van een eindgebruiker. Dit laatste wordt bepaald in deze macro op basis van instellingen bij de $apiuser in Brocade
    $apiuser: id van de API gebruiker
    $apises: API sessie id
    $eloi: id van de eindgebruiker
    $ctime: tijdstip van authenticatie
    $example: m4_setAPIeuserAuthenticated(UDapiuser,UDapises,"e:UA:185629")
    '''

    ««s ^ZAPI("eauth",$apiuser_"^"_$apises,$eloi)=$ctime»»
macro isAPIeuserAuthenticated($valid, $apiuser, $apises, $eloi, $rights="R"):
    '''
    $editfile: /api/application/api.d
    $synopsis:  Heeft een API gebruiker geldige rechten om gegevens van een eloi te zien/bewerken. Naast dat de validatie van de eindgebruiker wordt gecontroleerd, wordt er ook gekeken of de Brocade gebruiker die gekoppeld is aan $apiuser, toegang heeft tot het eindgebruikerssysteem van $eloi
    $valid: 1=geldige rechten anders 0
    $apiuser: id van de API gebruiker
    $apises: API sessie id
    $eloi: id van de eindgebruiker
    $rights: Waarde van de gevraagde access faciliteit : R = read W = read/write
    $example: m4_isAPIeuserAuthenticated(valid,"testuser","","e:UA:185629")
    '''

    ««s $valid=$$%valEusr^gbapi($apiuser,$apises,$eloi,$rights)»»

22.8. Extra documentatie

  • Documentatie gericht aan API gebruiker rond authenticatie-schema’s vind je hier
  • Documentatie gericht aan API gebruiker rond beschikbare apicalls vind je hier
  • PHP documentatie rond framework vind je hier

22.9. Nog op te lossen voor release

Notitie

Onderstaande issue moeten nog gedaan/opgelost worden voordat de API in release kan gaan

  • Eensgezindheid over look-and-feel van apicall, zowel hun naamsgeving en vormgeving respons.

  • Aanmaken van de nodige ‘basis’ apicalls met validationschema, nodige API parameters, m-routines en deze in b-file zetten

  • bij installatie van nieuwe meta via b-file, deze automatisch omzetten naar JSON (alle meta want kan zijn dat dit parameters is van apicall)

  • JSON opslag vanuit/visualie in textarea zoals bij json-schema bij apicall optimaliseren.

22.10. Bronnen

[rfc2617](1, 2) HTTP Authentication: Basic and Digest Access Authentication https://tools.ietf.org/html/rfc2617
[ModernPHP]Modern PHP: New Features and Good Practices - Josh Lockhart, - O’Reilly Media - February 2015
[SLIM]Slim a micro framework for PHP - Josh Lockhart, Andrew Smith, Rob Allen, and the Slim Framework Team - http://www.slimframework.com/
[PHPweb]PHP Web Services: APIs for the Modern Web - Lorna Jane Mitchell - O’Reilly Media - April 2013
[JSONschema]JSON Schema: core definitions and terminology - Internet Engineering Task Force (IETF) - https://tools.ietf.org/html/draft-zyp-json-schema