diff --git a/include/neuron/errcodes.h b/include/neuron/errcodes.h index 6b00b1970..609b76f54 100644 --- a/include/neuron/errcodes.h +++ b/include/neuron/errcodes.h @@ -71,6 +71,7 @@ typedef enum { NEU_ERR_NODE_NAME_EMPTY = 2015, NEU_ERR_NODE_TAGS_TOO_LONG = 2016, NEU_ERR_NODE_TAGS_TOO_MANY = 2017, + NEU_ERR_NODE_TAGS_INVALID = 2018, NEU_ERR_GROUP_ALREADY_SUBSCRIBED = 2101, NEU_ERR_GROUP_NOT_SUBSCRIBE = 2102, diff --git a/plugins/restful/adapter_handle.c b/plugins/restful/adapter_handle.c index 69e644ea7..1355bbd40 100644 --- a/plugins/restful/adapter_handle.c +++ b/plugins/restful/adapter_handle.c @@ -54,6 +54,29 @@ static int tags_length(const char *tags) return length; } +/* +'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '+', '=', '[', ']', '{', +'}', + ';', ':', '\'', '"', ',', '<', '>', '?', '\\', '|', '~', '`', '\t', +'\n', +*/ +static bool tags_check_symbol(const char *tags) +{ + for (size_t i = 0; i < strlen(tags); i++) { + char c = tags[i]; + if (c == '!' || c == '@' || c == '#' || c == '$' || c == '%' || + c == '^' || c == '&' || c == '*' || c == '(' || c == ')' || + c == '-' || c == '+' || c == '=' || c == '[' || c == ']' || + c == '{' || c == '}' || c == ';' || c == ':' || c == '\'' || + c == '"' || c == '<' || c == '>' || c == '?' || c == '\\' || + c == '|' || c == '~' || c == '`' || c == '\t' || c == '\n') { + return false; + } + } + + return true; +} + void handle_add_adapter(nng_aio *aio) { neu_plugin_t *plugin = neu_rest_get_plugin(); @@ -62,6 +85,10 @@ void handle_add_adapter(nng_aio *aio) aio, neu_json_add_node_req_t, neu_json_decode_add_node_req, { if (strlen(req->name) >= NEU_NODE_NAME_LEN) { CHECK_NODE_NAME_LENGTH_ERR; + } else if (req->tags != NULL && !tags_check_symbol(req->tags)) { + NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_TAGS_INVALID, { + neu_http_response(aio, error_code.error, result_error); + }); } else if (req->tags != NULL && tags_length(req->tags) > 5) { NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_TAGS_TOO_MANY, { neu_http_response(aio, error_code.error, result_error); @@ -663,26 +690,28 @@ void handle_put_node_tag(nng_aio *aio) NEU_PROCESS_HTTP_REQUEST_VALIDATE_JWT( aio, neu_json_update_node_tag_req_t, neu_json_decode_update_node_tag_req, { - int ret = 0; - neu_reqresp_head_t header = { 0 }; - neu_req_update_node_tag_t cmd = { 0 }; - - if (req->tags != NULL && tags_length(req->tags) > 5) { - NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_TAGS_TOO_MANY, { + if (req->tags != NULL && !tags_check_symbol(req->tags)) { + NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_TAGS_INVALID, { neu_http_response(aio, error_code.error, result_error); }); - return; - } - - header.ctx = aio; - header.type = NEU_REQ_UPDATE_NODE_TAG; - strcpy(cmd.node, req->name); - strcpy(cmd.tags, req->tags); - ret = neu_plugin_op(plugin, header, &cmd); - if (ret != 0) { - NEU_JSON_RESPONSE_ERROR(NEU_ERR_IS_BUSY, { - neu_http_response(aio, NEU_ERR_IS_BUSY, result_error); + } else if (req->tags != NULL && tags_length(req->tags) > 5) { + NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_TAGS_TOO_MANY, { + neu_http_response(aio, error_code.error, result_error); }); + neu_json_decode_update_node_tag_req_free(req); + } else { + neu_reqresp_head_t header = { 0 }; + neu_req_update_node_tag_t cmd = { 0 }; + header.ctx = aio; + header.type = NEU_REQ_UPDATE_NODE_TAG; + strcpy(cmd.node, req->name); + strcpy(cmd.tags, req->tags); + int ret = neu_plugin_op(plugin, header, &cmd); + if (ret != 0) { + NEU_JSON_RESPONSE_ERROR(NEU_ERR_IS_BUSY, { + neu_http_response(aio, NEU_ERR_IS_BUSY, result_error); + }); + } } }) } \ No newline at end of file diff --git a/src/core/node_manager.c b/src/core/node_manager.c index c36e1d15d..2d94c706c 100644 --- a/src/core/node_manager.c +++ b/src/core/node_manager.c @@ -290,7 +290,7 @@ UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type, bool tag_found = true; for (size_t i = 0; i < index; i++) { - if (strstr(el->tags, tag_split_array[i]) == NULL) { + if (strcmp(el->tags, tag_split_array[i]) != 0) { tag_found = false; break; } diff --git a/src/utils/http.c b/src/utils/http.c index ee1899ff4..02edae23f 100644 --- a/src/utils/http.c +++ b/src/utils/http.c @@ -340,6 +340,9 @@ int neu_http_response(nng_aio *aio, neu_err_code_e code, char *content) case NEU_ERR_USER_NO_PERMISSION: case NEU_ERR_INVALID_USER_LEN: case NEU_ERR_PORT_IN_USE: + case NEU_ERR_NODE_TAGS_TOO_MANY: + case NEU_ERR_NODE_TAGS_INVALID: + case NEU_ERR_NODE_TAGS_TOO_LONG: status = NNG_HTTP_STATUS_BAD_REQUEST; break; case NEU_ERR_FILE_NOT_EXIST: diff --git a/tests/ft/neuron/api.py b/tests/ft/neuron/api.py index 690d6b34b..7c60057f1 100644 --- a/tests/ft/neuron/api.py +++ b/tests/ft/neuron/api.py @@ -68,6 +68,19 @@ def add_node(node, plugin, params=None, jwt=config.default_jwt): body["params"] = params return requests.post(url=config.BASE_URL + '/api/v2/node', headers={"Authorization": jwt}, json=body) +@gen_check +def add_node_with_tags(node, plugin, tags=None, params=None, jwt=config.default_jwt): + body = {"name": node, "plugin": plugin} + if params: + body["params"] = params + if tags: + body["tags"] = tags + return requests.post(url=config.BASE_URL + '/api/v2/node', headers={"Authorization": jwt}, json=body) + +@gen_check +def update_node_tags(node, tags, jwt=config.default_jwt): + return requests.put(url=config.BASE_URL + '/api/v2/node/tag', headers={"Authorization": jwt}, json={"name": node, "tags": tags}) + @gen_check def update_node(node, new_name, jwt=config.default_jwt): diff --git a/tests/ft/neuron/error.py b/tests/ft/neuron/error.py index 9dac85b59..7a6f3cda6 100644 --- a/tests/ft/neuron/error.py +++ b/tests/ft/neuron/error.py @@ -33,6 +33,9 @@ NEU_ERR_NODE_NOT_ALLOW_UPDATE = 2013 NEU_ERR_NODE_NOT_ALLOW_MAP = 2014 NEU_ERR_NODE_NAME_EMPTY = 2015 +NEU_ERR_NODE_TAGS_TOO_LONG = 2016 +NEU_ERR_NODE_TAGS_TOO_MANY = 2017 +NEU_ERR_NODE_TAGS_INVALID = 2018 NEU_ERR_GROUP_ALREADY_SUBSCRIBED = 2101 NEU_ERR_GROUP_NOT_SUBSCRIBE = 2102 diff --git a/tests/ft/node/test_node_tag.py b/tests/ft/node/test_node_tag.py new file mode 100644 index 000000000..02581c705 --- /dev/null +++ b/tests/ft/node/test_node_tag.py @@ -0,0 +1,55 @@ +import neuron.api as api +from neuron.config import * +from neuron.common import * +from neuron.error import * + +class TestNodeTag: + + + @description(given="existent driver node", when="add tags to the node", then="add success") + def test_add_tags_to_node(self): + response = api.add_node_with_tags(node="modbus-tcp", plugin=PLUGIN_MODBUS_TCP, tags="tag1,tag2") + assert 200 == response.status_code + assert NEU_ERR_SUCCESS == response.json()['error'] + response = api.get_nodes(type=1) + assert 200 == response.status_code + assert "tag1,tag2" == response.json()['nodes'][0]['tags'] + + @description(given="existent driver node with tags", when="update tags of the node", then="update success") + def test_update_tags_of_node(self): + response = api.update_node_tags(node="modbus-tcp", tags="tag3,tag4,tag5") + assert 200 == response.status_code + assert NEU_ERR_SUCCESS == response.json()['error'] + response = api.get_nodes(type=1) + assert 200 == response.status_code + assert "tag3,tag4,tag5" == response.json()['nodes'][0]['tags'] + + @description(given="existent driver node with tags", when="update tags of the node to empty", then="update success") + def test_update_tags_of_node_to_empty(self): + response = api.update_node_tags(node="modbus-tcp", tags="") + assert 200 == response.status_code + assert NEU_ERR_SUCCESS == response.json()['error'] + response = api.get_nodes(type=1) + assert 200 == response.status_code + assert "" == response.json()['nodes'][0]['tags'] + + @description(given="existent driver node", when="add tags with invalid symbol", then="add failed") + def test_add_tags_with_invalid_symbol(self): + response = api.add_node_with_tags(node="modbus-tcp-2", plugin=PLUGIN_MODBUS_TCP, tags="tag1,tag@2") + assert 400 == response.status_code + assert NEU_ERR_NODE_TAGS_INVALID == response.json()['error'] + + @description(given="existent driver node", when="update tags with invalid symbol", then="update failed") + def test_update_tags_with_invalid_symbol(self): + response = api.add_node(node="modbus-tcp-2", plugin=PLUGIN_MODBUS_TCP) + assert 200 == response.status_code + assert NEU_ERR_SUCCESS == response.json()['error'] + response = api.update_node_tags(node="modbus-tcp-2", tags="tag#3,tag4") + assert 400 == response.status_code + assert NEU_ERR_NODE_TAGS_INVALID == response.json()['error'] + + @description(given="existent driver node", when="add tags exceeding the limit", then="add failed") + def test_add_tags_exceeding_limit(self): + response = api.add_node_with_tags(node="modbus-tcp-3", plugin=PLUGIN_MODBUS_TCP, tags="tag1,tag2,tag3,tag4,tag5,tag6") + assert 400 == response.status_code + assert NEU_ERR_NODE_TAGS_TOO_MANY == response.json()['error'] \ No newline at end of file