From 887da9bc7da426674605645e3d1368bdf0c48c46 Mon Sep 17 00:00:00 2001 From: Jakob Olsson <Jakob Olsson> Date: Thu, 28 Nov 2019 15:38:53 +0100 Subject: [PATCH] extend library to load schemas in memory --- CMakeLists.txt | 4 +- src/json-validator.h | 3 + src/schema.c | 252 +++++++++++++++++++++++++++++++++++++++++++ src/schema.h | 38 +++++++ src/wrapper.cpp | 55 ---------- 5 files changed, 296 insertions(+), 56 deletions(-) create mode 100644 src/schema.c create mode 100644 src/schema.h delete mode 100644 src/wrapper.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a89e487..3fa5e33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,9 @@ INTERFACE # and one for the validator add_library(json-validator SHARED - src/wrapper.cpp) + src/json-validator.cpp + src/schema.c + ) set_target_properties(json-validator PROPERTIES diff --git a/src/json-validator.h b/src/json-validator.h index c405e5b..279640e 100644 --- a/src/json-validator.h +++ b/src/json-validator.h @@ -4,6 +4,9 @@ extern "C" { #endif +int schema_validator_destroy(void); +int schema_validator_init(void); +bool json_object_validate_schema_inject_definitions(struct json_object *j_object, struct json_object *definitions, struct json_object *j_schema); bool json_object_validate_schema(struct json_object *j_obj, struct json_object *j_schema); #ifdef __cplusplus } diff --git a/src/schema.c b/src/schema.c new file mode 100644 index 0000000..8cb39fa --- /dev/null +++ b/src/schema.c @@ -0,0 +1,252 @@ +#include <libubox/avl.h> +#include <libubox/avl-cmp.h> +#include <libubox/blobmsg.h> +#include <libubox/blobmsg_json.h> +#include <libubox/utils.h> +#include <libubus.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> + +#include "common.h" +#include "schema.h" + +#define JSON_SCHEMA_DIR "/usr/share/rpcd/schemas" + +struct schema_context s_ctx; + +static void schema_flush_method(struct schema_method *s_method) +{ + lwsl_notice("cleaning method %s\n", (char *)s_method->avl.key); + + if (s_method->input) + free(s_method->input); + if (s_method->output) + free(s_method->output); + if (s_method->avl.key) + free((void *) s_method->avl.key); + + free(s_method); + return; +} + +static void schema_flush_methods(struct schema_object *s_object) +{ + struct schema_method *s_method, *tmp; + + lwsl_notice("flushing all methods of object %s\n", (char *)s_object->avl.key); + + avl_for_each_element_safe(&s_object->schema_methods, s_method, avl, tmp) { + avl_delete(&s_object->schema_methods, &s_method->avl); + schema_flush_method(s_method); + } + + return; +} + +static void schema_flush_object(struct schema_object *s_object) +{ + lwsl_notice("cleaning object %s\n", (char *)s_object->avl.key); + + schema_flush_methods(s_object); + free((void *)s_object->avl.key); + if (s_object->definitions) + free(s_object->definitions); + + if (s_object->regex) + regfree(&s_object->regex_exp); + + free(s_object); + + return; +} + +void schema_flush_objects(void) +{ + struct schema_object *s_object, *tmp; + + lwsl_notice("cleaning all schema objects\n"); + + avl_for_each_element_safe(&s_ctx->schema_objects, s_object, avl, tmp) { + avl_delete(&s_ctx->schema_objects, &s_object->avl); + schema_flush_object(s_object); + } + return; +} + +static void schema_parse_object(struct schema_object *schema_object, struct blob_buf *acl, struct blob_attr *object) +{ + struct blob_attr *method, *properties; + int rem, rem2; + struct schema_method *s_method; + + blobmsg_for_each_attr(properties, object, rem) { + if (strncmp(blobmsg_name(properties), "properties", 11)) + continue; + + //lwsl_notice("%s %d method name = %s\n", __func__, __LINE__, blobmsg_name(object)); + s_method = calloc(1, sizeof(*s_method)); + if (!s_method) + continue; + + s_method->avl.key = strdup(blobmsg_name(object)); + avl_insert(&schema_object->schema_methods, &s_method->avl); + + blobmsg_for_each_attr(method, properties, rem2) { + //lwsl_notice("%s %d name = %s\n", __func__, __LINE__, blobmsg_name(method)); + if (!strncmp(blobmsg_name(method), "input", 6)) { + s_method->input = blobmsg_format_json(method, true); + //lwsl_notice("input = %s\n", s_method->input); + } + else if (!strncmp(blobmsg_name(method), "output", 7)) { + s_method->output = blobmsg_format_json(method, true); + //lwsl_notice("output = %s\n", s_method->output); + } + } + } +} + +static void +schema_setup_file(const char *path) +{ + struct blob_buf acl = { 0 }; + struct blob_attr *root, *object; + int rem, rem2; + struct schema_object *schema_object; + char *definitions = NULL; + + blob_buf_init(&acl, 0); + + if (!blobmsg_add_json_from_file(&acl, path)) { + fprintf(stderr, "Failed to parse %s\n", path); + goto out; + } + + schema_object = calloc(1, sizeof(*schema_object)); + if (!schema_object) + goto out; + + avl_init(&schema_object->schema_methods, avl_strcmp, false, NULL); + + /* find the key properties of root */ + blob_for_each_attr(root, acl.head, rem) { + if (!strncmp(blobmsg_name(root), "regex", 6)) { + schema_object->regex = blobmsg_get_bool(root); + lwsl_notice("%s: regex enabled %d\n", __func__, schema_object->regex); + + } + + if (!strncmp(blobmsg_name(root), "object", 7)) { + schema_object->avl.key = strdup(blobmsg_data(root)); + if (!schema_object->avl.key) + goto out_flush; + } + + if (!strncmp(blobmsg_name(root), "definitions", 12)) + definitions = blobmsg_format_json(root, true); + + if (strncmp(blobmsg_name(root), "properties", 11)) + continue; + + /* iterate all methods */ + blobmsg_for_each_attr(object, root, rem2) { + if (blobmsg_type(object) != BLOBMSG_TYPE_TABLE) + continue; + + schema_parse_object(prog, schema_object, &acl, object); + } + } + + if (schema_object->regex) { + int rv; + lwsl_notice("parsing regex for %s!\n", schema_object->avl.key ); + rv = regcomp(&schema_object->regex_exp, (char *) schema_object->avl.key, 0); + if (rv) { + lwsl_notice("%s: invalid regex: %s, flushing validation schema!\n", __func__, (char *) schema_object->avl.key); + goto out_flush; + } + } + + if (!schema_object->avl.key) + goto out_flush; + + schema_object->definitions = definitions; + avl_insert(&s_ctx->schema_objects, &schema_object->avl); + /*avl_for_each_element(&s_ctx->schema_objects, schema_object, avl) { + lwsl_notice("%s %d key = %s\n", __func__, __LINE__, (char *) schema_object->avl.key); + }*/ +out: + blob_buf_free(&acl); + return; +out_flush: + schema_flush_object(schema_object); +} + +struct schema_method *schema_get_method_schema(const char *object, const char *method) +{ + struct schema_object *s_object; + struct schema_method *s_method; + + lwsl_notice("%s: object = %s method = %s\n", __func__, object, method); + + s_object = schema_get_object_schema(prog, object); + if (!s_object) + return NULL; + + avl_for_each_element(&s_object->schema_methods, s_method, avl) { + lwsl_notice("%s: method = %s, avl.key = %s\n", __func__, method, (char *)s_method->avl.key); + if (!strncmp(method, (char *) s_method->avl.key, METHOD_NAME_MAX_LEN)) + return s_method; + } + return NULL; +} + +const char *schema_get_definitions_schema(const char *object) +{ + struct schema_object *s_object; + + s_object = schema_get_object_schema(prog, object); + if (!s_object) + return NULL; + + return s_object->definitions; +} + +struct schema_object *schema_get_object_schema(const char *object) +{ + struct schema_object *s_object; + + lwsl_notice("%s: object = %s\n", __func__, object); + + avl_for_each_element(&s_ctx->schema_objects, s_object, avl) { + lwsl_notice("%s: object = %s, avl.key = %s, regex = %d\n", __func__, object, (char *)s_object->avl.key, s_object->regex); + if (s_object->regex) { + if (!regexec(&s_object->regex_exp, object, 0, NULL, 0)) + return s_object; + } else { + if (!strncmp(object, (char *) s_object->avl.key, OBJECT_NAME_MAX_LEN)) + return s_object; + } + } + + return NULL; +} + +void +schema_setup_json(void) +{ + int i; + glob_t gl; + + avl_init(&s_ctx->schema_objects, avl_strcmp, false, NULL); + + if (glob(JSON_SCHEMA_DIR "/*.json", 0, NULL, &gl)) + return; + + for (i = 0; i < gl.gl_pathc; i++) { + lwsl_notice("path = %s\n", gl.gl_pathv[i]); + schema_setup_file(prog, gl.gl_pathv[i]); + } + + globfree(&gl); +} diff --git a/src/schema.h b/src/schema.h new file mode 100644 index 0000000..8c2b01a --- /dev/null +++ b/src/schema.h @@ -0,0 +1,38 @@ +#ifndef SCHEMA_H +#define SCHEMA_H + +#define METHOD_NAME_MAX_LEN 64 +#define OBJECT_NAME_MAX_LEN 64 + +struct schema_context { + struct avl_tree schema_objects; +}; + +struct schema_method { + char method_name[METHOD_NAME_MAX_LEN]; + + //struct blob_attr input; + //struct blob_attr output; + + char *input; + char *output; + + struct avl_node avl; +}; + +struct schema_object { + char object_name[OBJECT_NAME_MAX_LEN]; + + char *definitions; + + struct avl_tree schema_methods; + struct avl_node avl; +}; + +struct schema_method *schema_get_method_schema(const char *object, const char *method); +const char *schema_get_definitions_schema(const char *object); +struct schema_object *schema_get_object_schema(const char *object); +void schema_setup_json(); +void schema_flush_objects(); + +#endif \ No newline at end of file diff --git a/src/wrapper.cpp b/src/wrapper.cpp deleted file mode 100644 index 83f9bdb..0000000 --- a/src/wrapper.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include <iostream> -#include <iomanip> -#include <stdbool.h> - -#include <json-c/json.h> -#include <json-schema.hpp> - -#include "json-validator.h" - -using nlohmann::json; -using nlohmann::json_uri; -using nlohmann::json_schema::json_validator; - -bool json_object_validate_schema(struct json_object *j_object, struct json_object *j_schema) -{ - std::cout << "hello" << std::endl; - const char *sch_str, *obj_str; - - obj_str = json_object_get_string(j_object); - sch_str = json_object_get_string(j_schema); - - std::cout << "obj_str " << obj_str << std::endl; - std::cout << "sch_str " << sch_str << std::endl; - - json sch = json::parse(sch_str); - json obj = json::parse(obj_str); - - /* json-parse the schema */ - - json_validator validator; // create validator - - try { - validator.set_root_schema(sch); // insert root-schema - } catch (const std::exception &e) { - std::cerr << "Validation of schema failed, here is why: " << e.what() << "\n"; - return 0; - } - - /* json-parse the people - with custom error handler */ - class custom_error_handler : public nlohmann::json_schema::basic_error_handler - { - void error(const nlohmann::json::json_pointer &ptr, const json &instance, const std::string &message) override - { - nlohmann::json_schema::basic_error_handler::error(ptr, instance, message); - std::cerr << "ERROR: '" << ptr << "' - '" << instance << "': " << message << "\n"; - } - }; - - custom_error_handler err; - validator.validate(obj, err); // validate the document - - if (err) - return 0; - return 1; -} \ No newline at end of file -- GitLab