Examples - REST Basic Authentication Example

Top  Previous  Next

//-----------------------------------------------------------------------------
// REST example using basic authentication
// Sending a request to /test will return a response containing information
// about the request as JSON.
// The client must provide the username "user" and the password "pass" to
//
//-----------------------------------------------------------------------------
INCLUDE rtcu.inc
 
#DEFINE EP_AUTH      1
#DEFINE EP_CONTENT   2
#DEFINE EP_LOGOUT    3
 
// Authentication callback
// Validates the username and password.
// If called from /logout, it will just return Unauthorized
// to make the client forget its current authentication parameters.
FUNCTION CALLBACK devicesAuthCallback : INT;
VAR_INPUT
  req         : SYSHANDLE;
  resp        : SYSHANDLE;
  arg         : DINT; // User argument indicating endpoint.
END_VAR;
VAR
  str, user, pass : STRING;
  i  : INT;
  rc : INT;
END_VAR;
  restReqClientAddressGet (req:=req, address:=str);
  DebugFmt(message:="Auth Request from "+str);
 
  // Enable basic authentication in the response
  rc := restRespBasicAuthSet(resp:=resp);
  DebugFmt(message:="restRespBasicAuthSet: \1", v1:=rc);
 
  // Read current authentication
  rc := restReqBasicAuthGet(req:=req, user:=user, pass:=pass);
  DebugFmt(message:="restReqBasicAuthGet: \1, "+user+":"+pass, v1:=rc);
 
  IF arg = EP_LOGOUT THEN
    restRespBodySetString(resp:=resp, str := "Logged out");
    // unauthorized, request basic authentication
    devicesAuthCallback := -1;
  ELSE
    IF user = "user" AND pass = "pass" THEN
        // Valid user, continue with content callback
        devicesAuthCallback:=0;
    ELSE
        restRespBodySetString(resp:=resp, str := "Invalid user");
        // unauthorized, request basic authentication
        devicesAuthCallback := -1;
    END_IF;
  END_IF;
END_FUNCTION;
 
 
// Content callback
// Creates a response containing the request parameters converted into JSON
FUNCTION CALLBACK devicesGetCallback: INT;
VAR_INPUT
  req          : SYSHANDLE;
  resp         : SYSHANDLE;
  arg          : DINT;
END_VAR;
VAR
  str, name    : STRING;
  rc, i        : INT;
  json, o, a   : SYSHANDLE;
  pos, sz, len : DINT;
  buf          : ARRAY [1..32] OF SINT;
END_VAR;
  // Create main JSON object
  jsonCreateObject(o:=json);
 
  // Headers
  jsonCreateObject(o:=o);
  i := 0;
  rc := restReqHeaderKey(req:=req, idx:=i, name:=name);
  WHILE rc > 0 DO
    str := "";
    restReqHeaderGet(req:=req, name:=name, value := str);
    jsonSetValueString(o:=o, key:=name, value:=str);
    i := i + 1;
    rc := restReqHeaderKey(req:=req, idx:=i, name:=name);
  END_WHILE;
  jsonSetValue(o:=json, key:="headers", value:=o);
  jsonFree(o:=o);
 
  // Queries
  jsonCreateObject(o:=o);
  i := 0;
  rc := restReqQueryKey(req:=req, idx:=i, name:=name);
  WHILE rc > 0 DO
    restReqQueryGet(req:=req, name:=name, value := str);
    jsonSetValueString(o:=o, key:=name, value:=str);
    i := i + 1;
    rc := restReqQueryKey(req:=req, idx:=i, name:=name);
  END_WHILE;
  jsonSetValue(o:=json, key:="queries", value:=o);
  jsonFree(o:=o);
 
  // Post parameters
  jsonCreateObject(o:=o);
  i := 0;
  rc := restReqPostKey(req:=req, idx:=i, name:=name);
  WHILE rc > 0 DO
    restReqPostGet(req:=req, name:=name, value := str);
    jsonSetValueString(o:=o, key:=name, value:=str);
    i := i + 1;
    rc := restReqPostKey(req:=req, idx:=i, name:=name);
  END_WHILE;
  jsonSetValue(o:=json, key:="posts", value:=o);
  jsonFree(o:=o);
 
  // Body size
  sz := restReqBodySize(req:=req);
  jsonSetValueInt(o:=json, key:="body-size", value:=sz);
 
  // Body as text
  str := "";
  restReqBodyGetString(req:=req, str:=str);
  jsonSetValueString(o:=json, key:="body-text", value:=str);
 
  // Raw body, split into smaller hex-strings.
  rc := jsonCreateArray(o:=a);  
  pos := 0;
  WHILE pos < sz DO
    len := restReqBodyGetRaw(req:=req, dst:=ADDR(buf), size := SIZEOF(buf), start := pos);
    IF len <= 0 THEN
        DebugFmt(message:="failed to get body: \4", v4:=len);
        EXIT;
    END_IF;
    str := "";
    FOR i := 1 TO len DO
        str := str+sintToHex(v:=buf[i]);
    END_FOR;
    jsonAddValueString(o:=a, value:=str);
     
    pos := pos + len;
  END_WHILE;
  jsonSetValue(o:=json, key:="body-raw", value:=a);
  rc := jsonFree(o:=a);
 
  // Set response
  rc := restRespBodySetJSON(resp:=resp, json:=json);
  DebugFmt(message:="restRespBodySetJSON: \1, ", v1:=rc);
 
  rc := jsonFree(o:=json);
  DebugFmt(message:="jsonFree: \1, ", v1:=rc);
 
  devicesGetCallback := 200;
END_FUNCTION;
 
 
 
// Send a request to the local server and show the response.
FUNCTION SendRequest;
VAR
  str, name    : STRING;
  req, resp    : SYSHANDLE;
  rc, i        : INT;
  pos, sz, len : DINT;
  buf          : ARRAY[1..16] OF SINT;
END_VAR;
  DebugMsg(message:="--------");
 
  rc := restReqCreate(req:=req, method := "POST",
    url := "http://127.0.0.1/test");
 
  rc := restReqQuerySet(req:=req, name := "query", value:="value 1");
 
  rc := restReqBodySetString(req:=req, str:="{$"test$":42}");
  rc := restReqHeaderSet(req:=req, name:="Accept", value:="application/json");
 
  // Set authentication
  rc := restReqBasicAuthSet(req:=req, user:="user", pass:="pass");
 
  // Send request
  rc := restClientRequest(req:=req, resp := resp);
  DebugFmt(message:="Send request: \1", v1:=rc);
  // Handle response
  IF rc = 200 THEN
    // success
   
    // Print headers
    i := 0;
    rc := restRespHeaderKey(resp:=resp, idx:=i, name:=name);
    WHILE rc > 0 DO
        restRespHeaderGet(resp:=resp, name:=name, value := str);
        DebugMsg(message:=" "+name+": "+str);
        i := i + 1;
        rc := restRespHeaderKey(resp:=resp, idx:=i, name:=name);
    END_WHILE;
 
    // Print body information
    sz := restRespBodySize(resp:=resp);
    DebugFmt(message:="body size: \4", v4:=sz);
 
    rc := restRespBodyGetString(resp:=resp, str := str);
    DebugFmt(message:="body: "+str);
 
    // Print raw body
    pos := 0;
    WHILE pos < sz DO
        len := restRespBodyGetRaw(resp:=resp, dst:=ADDR(buf), size := SIZEOF(buf), start := pos);
        IF len <= 0 THEN
          DebugFmt(message:="failed to get body: \4", v4:=len);
          EXIT;
        END_IF;
        str := dintToHex(v:=pos)+": ";
        FOR i := 1 TO len DO
          str := str+sintToHex(v:=buf[i])+" ";
        END_FOR;
        DebugMsg(message:=str);
 
        pos := pos + len;
    END_WHILE;
 
    rc := restRespFree(resp:=resp);
  ELSIF rc > 0 THEN
    rc := restRespBodyGetString(resp:=resp, str := str);
    DebugFmt(message:="Request failed: "+str);
  END_IF;
 
  rc := restReqFree(req:=req);
 
END_FUNCTION;
 
 
 
PROGRAM auth;
// These are the local variables of the program block
VAR
  rc   : INT;
  serv : SYSHANDLE;
  port : DINT := 80;
 
END_VAR;
  // Open LAN interface to provide access for external connections.
  netOpen(iface:=2);
 
  rc := restServerCreate(handle:=serv, port:=port);
  DebugFmt(message:="restServerCreate: \1", v1:=rc);
 
 
  // Add authentication endpoint with high priority
  rc := restServerEndpointAdd(handle:=serv, url_prefix:="/test",
    cb_func:=@devicesAuthCallback, cb_arg:=EP_AUTH);
  DebugFmt(message:="restServerEndpointAdd: \1", v1:=rc);
 
  // Add content endpoint with lower priority
  rc := restServerEndpointAdd(handle:=serv, url_prefix:="/test",
    cb_func:=@devicesGetCallback, cb_arg:=EP_CONTENT, prio:=1);
  DebugFmt(message:="restServerEndpointAdd: \1", v1:=rc);
 
  // Add logout endpoint, reusing authentication endpoint
  rc := restServerEndpointAdd(handle:=serv, url_prefix:="/logout",
    cb_func:=@devicesAuthCallback, cb_arg:=EP_LOGOUT);
  DebugFmt(message:="restServerEndpointAdd: \1", v1:=rc);
 
  // Start server
  rc := restServerStart(handle:=serv);
  DebugFmt(message:="restServerStart: \1", v1:=rc);
 
 
BEGIN
 
  // send local request
  SendRequest();
 
  Sleep(delay:=30000);
 
END;
 
END_PROGRAM;