On 23.5.2023 0.19, Heikki Vatiainen via radiator wrote:
On 22.5.2023 9.37, Greg Haverkamp via radiator wrote:

I get a JSON response that looks something like this:
{
"version": "LinOTP 2.11.2",
"jsonrpc": "2.0802",
"result": {
   "status": true,
   "value": false
},
"id": 0
}


I can create an example of how to access the contents of the "result" key. The default is to create a Perl hash for the keys but more complex structures require more configuration.

Here's a configuration snippet followed by a hook. Trace 5 and Debug are set because they allow logging the requests and responses sent over HTTP(S).

The RestAuthReplyDef parameters use values set by the hook. The first ones that use Auth-Type check item, trigger failure if "result" is not present in the JSON reply or one of "status" or "value" is false. This means the above JSON response triggers a failure.

The RestAuthReplyDef parameters that add X-Result-Status and X-Result-Value to request show how to something to the current request based on the JSON response.


Trace 5

<AuthBy REST>
      # Config parameters ...
      Debug

      # Call the hook code in this file.
      MapResponseHook file:"%D/rest-map-hook.pl"

      # How to handle a response from the server.
      #
      # With Auth-Type check items you can trigger reject here.
      RestAuthReplyDef auth_type_result, Auth-Type, check
      RestAuthReplyDef auth_type_status, Auth-Type, check
      RestAuthReplyDef auth_type_value,  Auth-Type, check

      # As an alternative, you can store the values in request or
      # reply and process them later.
      RestAuthReplyDef result_status, X-Result-Status, request
      RestAuthReplyDef result_value,  X-Result-Value,  request

      # Log the Radius request to show the new X-Result- attributes.
PostAuthHook sub { my $p = ${$_[0]}; main::log($main::LOG_DEBUG, "Request after AuthBy\n" . $p->dump(), $p); }
</AuthBy>



A rest-map-hook.pl sample is below. It first calls log to show how the default JSON to Perl mapping was done. It then makes additional keys available for the RestAuthReplyDef configuration parameters to use. Note that if a key, for example 'auth_type_result', is not present, the RestAuthReplyDef is skipped.

The log() calls are simply to aid debugging so that it's easier to see how JSON responses map to Perl data structures by default and ensure that the changes done within the hook are correct. With a hook, any complex JSON responses can be processed because the hook sees them in Perl mapped format.


use strict;
use warnings;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;

sub {
main::log($main::LOG_DEBUG, "Default processed response\n" . Dumper(@_));

    # $response_hash does not directly contain the mapped JSON
    # response. To update values RestAuthReplyDef configuration
    # parameter uses, modify 'server_response' within $response_hash.
    my $response_hash = $_[0];
    my $server_response = $response_hash->{server_response};

    # Successful reply must have 'result'.
    if ($server_response->{result})
    {
        # Trigger reject if 'status' or 'value' within 'result' is
        # false. Requires Auth-Type check item in the configuration.
$server_response->{auth_type_status} = 'Reject:Status is false' if !$server_response->{result}->{status}; $server_response->{auth_type_value} = 'Reject:Value is false' if !$server_response->{result}->{value};

        # 'status' and 'value' are JSON booleans that are mapped to
        # Perl objects. Convert them to plain numeric zero or one.
$server_response->{result_status} = $server_response->{result}->{status} ? 1 : 0; $server_response->{result_value} = $server_response->{result}->{value} ? 1 : 0;
    }
    else
    {
        # Trigger reject if there's no JSON object named 'result'
        $server_response->{auth_type_result} = 'Reject:No result in reply';
    }

    # Log the mapping to show the new keys 'result_value' and
    # 'result_status' and optionally 'auth_type' is 'status' within
    # 'result' was not true.
main::log($main::LOG_DEBUG, "Updated processed response\n" . Dumper(@_));

    return;
}



Here's a snippet from Radiator log. Note that JSON booleans map to Perl objects. Therefore the 'result_status' and 'result_value' are made to simple zero and one which are easy to compare. A string comparison against an object is unlikely to produce expected results. The zero and one values are shown in the message logged from PostAuthHook as values of X-Result-Value and X-Result-Status. A PostAuthHook, AuthLog, a subsequent AuthBy etc. can later use them as required.


Thu May 25 17:14:02 2023 330808: DEBUG: AuthREST 'rest-authby' '127.0.0.1 port 8888' received HTTP response:
HTTP/1.1 200 OK
Date: Mon, 23 May 2021 22:38:34 GMT
Content-Type: application/json
Content-Length: 112
Last-Modified: Wed, 08 Jan 2021 23:11:55 GMT
Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)
Accept-Ranges: bytes
Connection: close

{
"version": "LinOTP 2.11.2",
"jsonrpc": "2.0802",
"result": {
   "status": true,
   "value": false
},
"id": 0
}
Thu May 25 17:14:02 2023 331130: DEBUG: HTTPClientConnection '127.0.0.1-8888' Stream disconnected from 127.0.0.1 (127.0.0.1 port 8888)
Thu May 25 17:14:02 2023 331674: DEBUG: Default processed response
$VAR1 = {
          'server_response' => {
                                 'id' => 0,
                                 'jsonrpc' => '2.0802',
                                 'result' => {
'status' => bless( do{\(my $o = 1)}, 'JSON::PP::Boolean' ), 'value' => bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' )
                                             },
                                 'version' => 'LinOTP 2.11.2'
                               },
          'status_line' => '200 OK'
        };

Thu May 25 17:14:02 2023 331964: DEBUG: Updated processed response
$VAR1 = {
          'server_response' => {
'auth_type_value' => 'Reject:Value is false',
                                 'id' => 0,
                                 'jsonrpc' => '2.0802',
                                 'result' => {
'status' => bless( do{\(my $o = 1)}, 'JSON::PP::Boolean' ), 'value' => bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' )
                                             },
                                 'result_status' => 1,
                                 'result_value' => 0,
                                 'version' => 'LinOTP 2.11.2'
                               },
          'status_line' => '200 OK'
        };

Thu May 25 17:14:02 2023 332250: DEBUG: Handling with Radius::AuthREST: rest-authby Thu May 25 17:14:02 2023 332642: DEBUG: Radius::AuthREST looks for match with 'mikem' [mikem] Thu May 25 17:14:02 2023 332882: DEBUG: Radius::AuthREST REJECT_IMMEDIATE: Value is false: 'mikem' [mikem]
Thu May 25 17:14:02 2023 333270: DEBUG: Request after AuthBy
Code:       Access-Request
Identifier: 117
Authentic:  <11>Im<168>#<202><149><243>$N><217><131>P-<197>
Attributes:
        User-Name = "mikem"
        Service-Type = Framed-User
        NAS-IP-Address = 203.63.154.1
        NAS-Identifier = "203.63.154.1"
        NAS-Port = 1234
        Called-Station-Id = "123456789"
        Calling-Station-Id = "987654321"
        NAS-Port-Type = Async
User-Password = <138><237><247><10>)<203>g<127><235>I<194><129>D<133>YX
        X-Result-Status = 1
        X-Result-Value = 0

Thu May 25 17:14:02 2023 333467: DEBUG: AuthBy REST result: REJECT_IMMEDIATE, Value is false Thu May 25 17:14:02 2023 333761: INFO: Access rejected for mikem: Value is false




I'll add the above to goodies.

Thanks,
Heikki

--
Heikki Vatiainen
OSC, makers of Radiator
Visit radiatorsoftware.com for Radiator AAA server software
_______________________________________________
radiator mailing list
[email protected]
https://lists.open.com.au/mailman/listinfo/radiator

Reply via email to