From: Matthias Kruk Date: Sat, 12 Aug 2023 05:13:52 +0000 (+0900) Subject: include/ipc: Add JSON Schema-based data validation X-Git-Url: https://git.corax.cc/?a=commitdiff_plain;h=fcd1afa418585aaf7ee886edb621a0a737efd1bc;p=toolbox include/ipc: Add JSON Schema-based data validation When an application receives a message from `ipc_endpoint_recv()', it has to validate the contents of the message. It would be more convenient if the validation could be performed by the ipc module, so that the `ipc_endpoint_recv()' function only returns valid messages to the caller. This commit modifies the ipc module so that the user may set a JSON Schema that is used by the ipc module to validate the data of incoming messages. When a schema has been set, only messages that passed validation will be returned by `ipc_endpoint_recv()'. --- diff --git a/docs/ipc.md b/docs/ipc.md index ea6ce52..91010de 100644 --- a/docs/ipc.md +++ b/docs/ipc.md @@ -22,11 +22,13 @@ sent by one module cannot be read by the other. | [ipc_encode()](#ipc_encode) | Encode an IPC message | | [ipc_endpoint_close()](#ipc_endpoint_close) | Close an IPC endpoint | | [ipc_endpoint_foreach_message()](#ipc_endpoint_foreach_message) | Iterate over an endpoint's message queue ] | +| [ipc_endpoint_get_data_schema()](#ipc_endpoint_get_data_schema) | Get the JSON Schema used to validate message data | | [ipc_endpoint_get_subscriptions()](#ipc_endpoint_get_subscriptions) | Get the list of topics that an endpoint is subscribed to | | [ipc_endpoint_open()](#ipc_endpoint_open) | Open an endpoint for IPC messaging | | [ipc_endpoint_publish()](#ipc_endpoint_publish) | Publish a message to a topic | | [ipc_endpoint_recv()](#ipc_endpoint_recv) | Receive a point-to-point or pub-sub message | | [ipc_endpoint_send()](#ipc_endpoint_send) | Send a point-to-point message to another endpoint | +| [ipc_endpoint_set_data_schema()](#ipc_endpoint_set_data_schema) | Set the JSON Schema used to validate message data | | [ipc_endpoint_subscribe()](#ipc_endpoint_subscribe) | Subscribe an endpoint to one or more topics | | [ipc_endpoint_unsubscribe()](#ipc_endpoint_unsubscribe) | Unsubscribe an endpoint from one or more topics | | [ipc_get_root()](#ipc_get_root) | Return the path to the IPC directory | @@ -40,6 +42,7 @@ sent by one module cannot be read by the other. | [ipc_msg_get_user()](#ipc_msg_get_user) | Get the user name of a message's sender | | [ipc_msg_get_version()](#ipc_msg_get_version) | Get the protocol version of a message | | [ipc_msg_new()](#ipc_msg_new) | Generate a new IPC message | +| [ipc_msg_validate_data()](#ipc_msg_validate_data) | Use a JSON Schema to validate the data in a message | ## ipc_decode() @@ -195,6 +198,41 @@ This function does not write to standard output. This function does not write to standard error. +## ipc_endpoint_get_data_schema() + +Get the JSON Schema used to validate message data + +### Synopsis + + ipc_endpoint_get_data_schema "$endpoint" + +### Description + +The `ipc_endpoint_get_data_schema()` function writes the URL of the JSON Schema that is used to validate +message data received by `endpoint` to standard output. If no schema was set, no data will be written to +standard output and an error will be returned. + +### Return value + +| Return value | Meaning | +|--------------|-------------------------------------------------------------| +| 0 | The URL of the schema was written to standard output | +| 1 | The endpoint does not use a schema to validate message data | + +### Standard input + +This function does not read from standard input. + +### Standard output + +If a data validation schema is set in the endpoint, the URL of that schema will be written to +standard output. + +### Standard error + +This function does not write to standard error. + + ## ipc_endpoint_get_subscriptions() Get the list of topics that an endpoint is subscribed to @@ -315,12 +353,18 @@ endpoint, it will wait for up to `timeout` seconds. If `timeout` is zero, the fu return immediately without waiting for messages. If `timeout` was omitted, the function will wait indefinitely. +If a message data validation schema was set using `ipc_endpoint_set_data_schema()`, data +in incoming messages will be validated against that schema. If a received message contains +data that could not be validated, the message will be dropped and `ipc_endpoint_recv()` will +return an error. + ### Return value -| Return value | Meaning | -|--------------|--------------------| -| 0 | Success | -| 1 | A timeout occurred | +| Return value | Meaning | +|--------------|-----------------------------------------------| +| 0 | Success | +| 1 | A timeout occurred | +| 2 | A message containing invalid data was dropped | ### Standard input @@ -368,6 +412,42 @@ This function does not write to standard output. In case of an error, an error message will be written to standard error. +## ipc_endpoint_set_data_schema() + +Set the JSON Schema used to validate message data + +### Synopsis + + ipc_endpoint_set_data_schema "$endpoint" "$schema" + +### Description + +The `ipc_endpoint_set_data_schema()` function enables message data validation in the endpoint +`endpoint` and assigns it the JSON Schema referenced by `schema`. Any successive calls to +`ipc_endpoint_recv()` will use the JSON Schema in `schema` to validate the data portion of +incoming messages, and drop incoming messages if the data could not be validated. + + +### Return value + +| Return value | Meaning | +|--------------|-------------------------------------------------| +| 0 | The data validation schema was successfully set | +| 1 | The data validation schema could not be set | + +### Standard input + +This function does not read from standard input. + +### Standard output + +This function does not write to standard output. + +### Standard error + +In case of an error, an error message is written to standard error. + + ## ipc_endpoint_subscribe() Subscribe an endpoint to one or more topics @@ -805,3 +885,36 @@ Upon success, the generated message is written to standard output. ### Standard error An error is written to standard error if the message could not be generated. + + +## ipc_msg_validate_data() + +Use a JSON Schema to validate the data in a message + +### Synopsis + + ipc_msg_validate_data "$message" "$schema" + +### Description + +The `ipc_msg_validate_data()` function validates the data contained in the +message `message` using the JSON Schema referenced by `schema`. + +### Return value + +| Return value | Meaning | +|--------------|-----------------------------------| +| 0 | The message contains valid data | +| 1 | The message contains invalid data | + +### Standard input + +This function does not read from standard input. + +### Standard output + +This function does not write to standard output. + +### Standard error + +An error is written to standard error if the message data could not be validated. diff --git a/include/ipc.sh b/include/ipc.sh index 718312b..5bc11d7 100644 --- a/include/ipc.sh +++ b/include/ipc.sh @@ -35,6 +35,7 @@ __init() { "msg_get_timestamp" \ "msg_get_data" \ "msg_get_topic" \ + "msg_validate_data" \ "encode" \ "decode" \ "endpoint_open" \ @@ -46,6 +47,8 @@ __init() { "endpoint_get_subscriptions" \ "endpoint_publish" \ "endpoint_foreach_message" \ + "endpoint_set_data_schema" \ + "endpoint_get_data_schema" return 0 } @@ -441,6 +444,20 @@ ipc_msg_get_topic() { return 0 } +ipc_msg_validate_data() { + local msg="$1" + local schema="$2" + + local errors + + if ! errors=$(toolbox-json-validate "$schema" <(ipc_msg_get_data "$msg") 2>&1); then + log_highlight "Invalid data in IPC message" <<< "$errors" | log_info + return 1 + fi + + return 0 +} + ipc_msg_get_signature() { local msg="$1" @@ -600,6 +617,7 @@ ipc_endpoint_recv() { local queue local msg + local schema queue="$(ipc_get_root)/$endpoint/queue" @@ -607,6 +625,11 @@ ipc_endpoint_recv() { return 1 fi + if schema=$(ipc_endpoint_get_data_schema "$endpoint") && + ! ipc_msg_validate_data "$msg" "$schema"; then + return 2 + fi + echo "$msg" return 0 } @@ -792,3 +815,37 @@ ipc_endpoint_foreach_message() { return 0 } + +ipc_endpoint_set_data_schema() { + local endpoint="$1" + local schema="$2" + + local schema_file + + schema_file="$(ipc_get_root)/$endpoint/schema" + + if [[ -n "$schema" ]]; then + if ! printf '%s\n' "$schema" > "$schema_file"; then + log_info "Could not set data validation schema in $schema_file" + return 1 + fi + elif ! rm -f "$schema_file"; then + return 2 + fi + + return 0 +} + +ipc_endpoint_get_data_schema() { + local endpoint="$1" + + local schema_file + + schema_file="$(ipc_get_root)/$endpoint/schema" + + if ! cat "$schema_file" 2>/dev/null; then + return 1 + fi + + return 0 +}