From bd8914cc2d4f676ab9c16000858fa5584496420f Mon Sep 17 00:00:00 2001 From: Somin Kim Date: Thu, 24 Aug 2017 23:49:51 +0900 Subject: [PATCH] [trigger] Added template verification Change-Id: Ic05ef1139013282251debd761469ee3613f665e1 Signed-off-by: Somin Kim --- src/trigger/CustomTemplate.cpp | 192 +++++++++++++++++++++++++++++++++++++--- src/trigger/CustomTemplate.h | 11 ++- src/trigger/context_trigger.cpp | 16 ++-- 3 files changed, 197 insertions(+), 22 deletions(-) diff --git a/src/trigger/CustomTemplate.cpp b/src/trigger/CustomTemplate.cpp index 780a86e..4741fb5 100644 --- a/src/trigger/CustomTemplate.cpp +++ b/src/trigger/CustomTemplate.cpp @@ -15,8 +15,17 @@ */ #include +#include #include "CustomTemplate.h" +#define CT_KEY_MIN "minimum" +#define CT_KEY_MAX "maximum" +#define CT_KEY_TYPE "type" +#define CT_TYPE_INTEGER "integer" +#define CT_TYPE_STRING "string" +#define CT_TYPE_ENUM "enum" +#define CT_TYPE_DOUBLE "double" + static std::list __instances; CustomTemplate::CustomTemplate(const std::string& name, const Json::Value& templateJson) : @@ -30,35 +39,192 @@ const std::string& CustomTemplate::getName() const return __name; } -bool CustomTemplate::match(const std::string& fact) +int CustomTemplate::match(const std::string& fact) +{ + // true if the given fact is valid w.r.t. the template + Json::Reader reader; + Json::Value factJson; + + // Error: Invalid Json + if (!reader.parse(fact, factJson)) { + _E("Fact: invalid json"); + return CONTEXT_TRIGGER_ERROR_INVALID_PARAMETER; + } + + // Error: Invalid fact + IF_FAIL_RETURN_TAG(isValidFact(__templateJson, factJson), CONTEXT_TRIGGER_ERROR_INVALID_DATA, _E, "Invalid fact"); + + return CONTEXT_TRIGGER_ERROR_NONE; +} + +bool CustomTemplate::isValidFact(const Json::Value& tmplJson, const Json::Value& factJson) +{ + Json::Value::Members keys = factJson.getMemberNames(); + + std::string dataType; + for (auto& key : keys) { + // Get type + if (tmplJson[key].isMember(CT_TYPE_ENUM)) { + dataType = CT_TYPE_ENUM; + } else if (tmplJson[key].isMember(CT_KEY_TYPE)) { + dataType = tmplJson[key][CT_KEY_TYPE].asString(); + } + + if (dataType == CT_TYPE_INTEGER) { + IF_FAIL_RETURN_TAG(factJson[key].isInt(), false, _E, "Custom fact: Invalid data type"); + + int val = factJson[key].asInt(); + if (tmplJson[key].isMember(CT_KEY_MIN)) { + IF_FAIL_RETURN_TAG(val >= tmplJson[key][CT_KEY_MIN].asInt(), false, _E, "Custom fact: Invalid value"); + } + if (tmplJson[key].isMember(CT_KEY_MAX)) { + IF_FAIL_RETURN_TAG(val <= tmplJson[key][CT_KEY_MAX].asInt(), false, _E, "Custom fact: Invalid value"); + } + } else if (dataType == CT_TYPE_STRING) { + IF_FAIL_RETURN_TAG(factJson[key].isString(), false, _E, "Custom fact: Invalid data type"); + } else if (dataType == CT_TYPE_ENUM) { + IF_FAIL_RETURN_TAG(factJson[key].isString(), false, _E, "Custom fact: Invalid data type"); + + std::string val = factJson[key].asString(); + + bool found = false; + Json::Value::Members tmplValues; + for (auto& tmplValue : tmplValues) { + if (tmplValue == val) + found = true; + } + + IF_FAIL_RETURN_TAG(found, false, _E, "Custom fact: Invalid value"); + } else { + _E("Custom fact: Invalid data type"); + return false; + } + } + return true; +} + +bool CustomTemplate::isValidTemplate(const Json::Value& tmplJson) { - //TODO: true if the given fact is valid w.r.t. the template + bool success = false; + Json::Value::Members keys = tmplJson.getMemberNames(); + + std::string dataType; + for (auto& key : keys) { + // Get type + if (tmplJson[key].isMember(CT_TYPE_ENUM)) { + success = tmplJson[key][CT_TYPE_ENUM].isArray(); + IF_FAIL_RETURN_TAG(success, false, _E, "Invalid template"); + dataType = CT_TYPE_ENUM; + } else if (tmplJson[key].isMember(CT_KEY_TYPE)) { + dataType = tmplJson[key][CT_KEY_TYPE].asString(); + IF_FAIL_RETURN_TAG(dataType == CT_TYPE_INTEGER || dataType == CT_TYPE_STRING, + false, _E, "Invalid template"); + } + + if (dataType == CT_TYPE_INTEGER) { + success = checkTemplateInt(tmplJson[key]); + IF_FAIL_RETURN(success, false); + } else if (dataType == CT_TYPE_STRING) { + success = checkTemplateString(tmplJson[key]); + IF_FAIL_RETURN(success, false); + } else if (dataType == CT_TYPE_ENUM) { + success = checkTemplateEnum(tmplJson[key]); + IF_FAIL_RETURN(success, false); + } + } + return true; } -bool CustomTemplate::add(const std::string& name, const std::string& attrTmpl) +bool CustomTemplate::checkTemplateInt(const Json::Value& elem) { - IF_FAIL_RETURN(!get(name), false); + bool min = false; + bool max = false; + int minVal = 0; + int maxVal = 0; + + Json::Value::Members elemKeys = elem.getMemberNames(); + for (auto& elemKey : elemKeys) { + if (elemKey == CT_KEY_MIN) { + min = true; + minVal = elem[elemKey].asInt(); + } else if (elemKey == CT_KEY_MAX) { + max = true; + maxVal = elem[elemKey].asInt(); + } else { + IF_FAIL_RETURN_TAG(elemKey == CT_KEY_TYPE, false, _E, "invalid key"); + } + } + + if (min && max) { + IF_FAIL_RETURN_TAG(minVal <= maxVal, false, _E, "Invalid min, max value"); + } + + return true; +} + +bool CustomTemplate::checkTemplateString(const Json::Value& elem) +{ + Json::Value::Members elemKeys = elem.getMemberNames(); + + for (auto& elemKey : elemKeys) { + IF_FAIL_RETURN_TAG(elemKey == CT_KEY_TYPE, false, _E, "invalid key"); + } + + return true; +} + +bool CustomTemplate::checkTemplateEnum(const Json::Value& elem) +{ + Json::Value::Members elemKeys = elem.getMemberNames(); + + for (auto& elemKey : elemKeys) { + if (elemKey == CT_TYPE_ENUM) { + IF_FAIL_RETURN_TAG(!elem[CT_TYPE_ENUM].empty(), false, _E, "Invalid enum"); + + for (unsigned int i = 0; i < elem[CT_TYPE_ENUM].size(); i++) { + IF_FAIL_RETURN_TAG(elem[CT_TYPE_ENUM][i].isString(), false, _E, "Enum value sholud be string"); + } + } else { + IF_FAIL_RETURN_TAG(elemKey == CT_KEY_TYPE, false, _E, "invalid key"); + } + } + + return true; +} + +int CustomTemplate::add(const std::string& name, const std::string& attrTmpl) +{ static std::regex nameRegex(R"~(^[\w-\._\/]+$)~", std::regex::optimize); - IF_FAIL_RETURN_TAG(std::regex_match(name, nameRegex), false, _E, "Invalid name"); + IF_FAIL_RETURN_TAG(std::regex_match(name, nameRegex), CONTEXT_TRIGGER_ERROR_INVALID_PARAMETER, _E, "Invalid name"); Json::Reader reader; Json::Value tmplJson; - try { - reader.parse(attrTmpl, tmplJson); - } catch (const Json::Exception& e) { - _E("Exception: %s", e.what()); - return false; + // Error: Invalid Json + if (!reader.parse(attrTmpl, tmplJson)) { + _E("Template: invalid json"); + return CONTEXT_TRIGGER_ERROR_INVALID_PARAMETER; } - //TODO: further validity check of the template - // Does it contain all necessary fields, ...? + // Error: already exist + CustomTemplate* tmpl = get(name); + if (tmpl) { + if (tmplJson == tmpl->__templateJson) + return CONTEXT_TRIGGER_ERROR_NONE; + else { + _E("Template already exists"); + return CONTEXT_TRIGGER_ERROR_DATA_EXIST; + } + } + + // Error: Invalid template + IF_FAIL_RETURN_TAG(isValidTemplate(tmplJson), CONTEXT_TRIGGER_ERROR_INVALID_DATA, _E, "Invalid template"); __instances.emplace_back(name, tmplJson); - return true; + return CONTEXT_TRIGGER_ERROR_NONE; } void CustomTemplate::remove(const std::string& name) diff --git a/src/trigger/CustomTemplate.h b/src/trigger/CustomTemplate.h index 751202d..2bbf579 100644 --- a/src/trigger/CustomTemplate.h +++ b/src/trigger/CustomTemplate.h @@ -27,12 +27,19 @@ public: CustomTemplate(const std::string& name, const Json::Value& templateJson); const std::string& getName() const; - bool match(const std::string& fact); + int match(const std::string& fact); - static bool add(const std::string& name, const std::string& attrTmpl); + static int add(const std::string& name, const std::string& attrTmpl); static void remove(const std::string& name); static CustomTemplate* get(const std::string& name); + static bool isValidTemplate(const Json::Value& tmplJson); + static bool checkTemplateInt(const Json::Value& elem); + static bool checkTemplateString(const Json::Value& elem); + static bool checkTemplateEnum(const Json::Value& elem); + static bool isValidFact(const Json::Value& tmplJson, const Json::Value& factJson); + + private: std::string __name; Json::Value __templateJson; diff --git a/src/trigger/context_trigger.cpp b/src/trigger/context_trigger.cpp index 0a869af..730bbac 100644 --- a/src/trigger/context_trigger.cpp +++ b/src/trigger/context_trigger.cpp @@ -905,11 +905,8 @@ EXPORT_API int context_trigger_custom_register(const char* name, const char* att INIT_SCHED; ASSERT_NOT_NULL(name && attr_template); - //TODO: Is it allowed to overwrite a template? - CustomTemplate::remove(name); - - bool success = CustomTemplate::add(name, attr_template); - IF_FAIL_RETURN_TAG(success, CONTEXT_TRIGGER_ERROR_INVALID_DATA, _E, "Invalid template"); + int error = CustomTemplate::add(name, attr_template); + IF_FAIL_RETURN_TAG(error == E_NONE, error, _E, "Failed to add template"); const char* pkgId = __get_pkg_id(); IF_FAIL_RETURN_TAG(pkgId, E_SUPPORT, _E, "PkgId is required"); @@ -927,6 +924,9 @@ EXPORT_API int context_trigger_custom_unregister(const char* name) INIT_SCHED; ASSERT_NOT_NULL(name); + CustomTemplate* customTemplate = CustomTemplate::get(name); + IF_FAIL_RETURN_TAG(customTemplate, E_SUPPORT, _E, "Unknown custom name"); + CustomTemplate::remove(name); const char* pkgId = __get_pkg_id(); @@ -945,8 +945,10 @@ EXPORT_API int context_trigger_custom_publish(const char* name, const char* fact ASSERT_NOT_NULL(name && fact); CustomTemplate* customTemplate = CustomTemplate::get(name); - IF_FAIL_RETURN_TAG(customTemplate, E_PARAM, _E, "Unknown custom name"); - IF_FAIL_RETURN(customTemplate->match(fact), CONTEXT_TRIGGER_ERROR_INVALID_DATA); + IF_FAIL_RETURN_TAG(customTemplate, E_SUPPORT, _E, "Unknown custom name"); + + int error = customTemplate->match(fact); + IF_FAIL_RETURN_TAG(error == E_NONE, error, _E, "Failed to publish fact"); const char* pkgId = __get_pkg_id(); IF_FAIL_RETURN_TAG(pkgId, E_SUPPORT, _E, "PkgId is required"); -- 2.7.4