Mattermost Slash Command Validator
Subflow with use case example.
Validates token, command, parameters and access. Details are described in subflow docs.
[{"id":"21a69898.c352a8","type":"subflow","name":"Validate cmd","info":"","category":"Mattermost","in":[{"x":60,"y":40,"wires":[{"id":"b2141708.806bc8"}]}],"out":[{"x":840,"y":40,"wires":[{"id":"e318a9f2.407598","port":0}]}],"env":[],"color":"#579eff","icon":"node-red/comment.svg"},{"id":"e318a9f2.407598","type":"function","z":"21a69898.c352a8","name":"(Subflow) Validator","func":"// Validate token\nif (msg.payload.cmd_payload.token !== msg.payload.token) {\n node.error(\"Invalid Mattermost command token.\", msg);\n return;\n}\n\n// Validate channel\nlet is_channel_on_the_list = msg.payload.channels.list.includes(msg.payload.cmd_payload.channel_name) ? true : false;\nlet is_white_list = msg.payload.channels.mode === \"black_list\" ? false : true;\n\nif (!is_white_list && is_channel_on_the_list || is_white_list && !is_channel_on_the_list) {\n node.error(\"Channel is not authorized to execute this command.\", msg);\n return;\n}\n\n// Validate user\nlet is_user_on_the_list = msg.payload.users.list.includes(msg.payload.cmd_payload.user_name) ? true : false;\nis_white_list = msg.payload.users.mode === \"black_list\" ? false : true;\n\nif (!is_white_list && is_user_on_the_list || is_white_list && !is_user_on_the_list) {\n node.error(\"User is not authorized to execute this command.\", msg);\n return;\n}\n\n// Validate args\nlet re = new RegExp(msg.payload.re_validator, 'gi');\nlet valid_args = msg.payload.cmd_payload.text.match(re);\nif (!valid_args) {\n node.error(`Invalid arguments: ${msg.payload.cmd_payload.text}`,msg);\n return;\n}\nmsg.payload = {};\nmsg.payload.cmd_args = valid_args[0];\n\nreturn msg;","outputs":1,"noerr":0,"x":690,"y":40,"wires":[[]]},{"id":"b2141708.806bc8","type":"change","z":"21a69898.c352a8","name":"Set JSON Schema Validation","rules":[{"t":"set","p":"schema","pt":"msg","to":"{\"title\":\"Mattermost Command Validator - Payload Validation Schema\",\"type\":\"object\",\"additionalProperties\":false,\"required\":[\"token\",\"channels\",\"users\",\"groups\",\"re_validator\",\"cmd_payload\"],\"properties\":{\"token\":{\"type\":\"string\",\"description\":\"Mattermost command token\"},\"channels\":{\"type\":\"object\",\"properties\":{\"mode\":{\"type\":\"string\",\"description\":\"Three possible values empty string/white_list/black_list. Defines how to treat list.\",\"pattern\":\"^$|^white_list|black_list$\"},\"list\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}}},\"users\":{\"type\":\"object\",\"properties\":{\"mode\":{\"type\":\"string\",\"description\":\"Three possible values empty string/white_list/black_list. Defines how to treat list.\",\"pattern\":\"^$|^white_list|black_list$\"},\"list\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}}},\"groups\":{\"type\":\"object\",\"properties\":{\"mode\":{\"type\":\"string\",\"description\":\"Three possible values empty string/white_list/black_list. Defines how to treat list.\",\"pattern\":\"^$|^white_list|black_list$\"},\"list\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}}},\"re_validator\":{\"type\":\"string\",\"description\":\"regex for args validation\"},\"cmd_payload\":{\"type\":\"object\",\"description\":\"Mattermost command payload\",\"additionalProperties\":true,\"required\":[\"token\",\"channel_name\",\"text\",\"user_name\"],\"properties\":{\"channel_name\":{\"type\":\"string\"},\"token\":{\"type\":\"string\"},\"text\":{\"type\":\"string\",\"description\":\"Command args\"},\"user_name\":{\"type\":\"string\"}}}}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":240,"y":40,"wires":[["901e2676.7d3b18"]]},{"id":"901e2676.7d3b18","type":"json","z":"21a69898.c352a8","name":"Validate payload","property":"payload","action":"obj","pretty":false,"x":480,"y":40,"wires":[["e318a9f2.407598"]]},{"id":"2953933d.d7319c","type":"inject","z":"8d38716.c512b9","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":120,"y":1820,"wires":[["d9461bb.f5df7e8"]]},{"id":"d9461bb.f5df7e8","type":"change","z":"8d38716.c512b9","name":"Mattermost Command Payload","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"channel_id\":\"3azjhqdxytbntpgjkex1g3xbpc\",\"channel_name\":\"general\",\"command\":\"/purchases\",\"response_url\":\"https://mattermost.digitaloak.it/hooks/commands/sgcedqm1xfb8iy6hxo7hny3hwa\",\"team_domain\":\"sales\",\"team_id\":\"hjmthfxfd7bktk6r15qfs6q5jy\",\"text\":\"check doc PAR/CA/19/12311\",\"token\":\"9yex4wtwbid8tbvy3xu5ufc37e\",\"trigger_id\":\"NXk1ZXRuNsR0YmRjYnkzM3VvODNrN2F0bnc6dWVwczdpOHBpaWJuZnEzYWlicWFmbWhmNWU6MTU3NzE0Njg4NzEwODpNRVFDSUdVTk1UNmxUbGZxcWlqQU9ycG13OG5oblJHUEZHRmtkNFhXbkhkME1mNUlBaUFPTlBuSkZzbnR2S3ljVENMei9Zek5LVHM1bzFlTEYzdWMyT3Z1dEdHYVNBPT0=\",\"user_id\":\"ueps7i8piibnfq3qibqafmhf5e\",\"user_name\":\"tmdoit\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":1820,"wires":[["df13adaa.10135"]]},{"id":"df13adaa.10135","type":"function","z":"8d38716.c512b9","name":"Prepare command for validation","func":"let find_doc_cmd = \"^check doc (?:FS|PAR|ZS)\\\\/\\\\w{2,5}\\\\/\\\\d{2}\\\\/\\\\d{1,7}$\";\nlet find_sn_cmd = \"^check sn [\\\\d\\\\w]{12,15}$\";\nlet find_user_id_cmd = \"^check user_id \\\\d{1,8}$\";\nlet find_status_cmd = \"^check status (?:rejected|accepted|pending)$\";\n\nlet mttm_validator = {\n \"token\":\"9yex4wtwbid8tbvy3xu5ufc37e\",\n \"channels\": { \n \"mode\":\"\", \n \"list\":[\"general\"]\n },\n \"users\": { \n \"mode\":\"white_list\", \n \"list\":[\"tmdoit\"]\n },\n \"groups\": { \n \"mode\":\"black_list\", \n \"list\":[]\n },\n \"re_validator\" : find_doc_cmd + \"|\" + find_sn_cmd + \"|\" + find_user_id_cmd + \"|\" + find_status_cmd,\n \"cmd_payload\": msg.payload\n}\nmsg.payload = mttm_validator;\nreturn msg;","outputs":1,"noerr":0,"x":650,"y":1820,"wires":[["782dd3e4.49c60c"]]},{"id":"782dd3e4.49c60c","type":"subflow:21a69898.c352a8","z":"8d38716.c512b9","name":"","env":[],"x":890,"y":1820,"wires":[["723d6555.f31b9c"]],"icon":"font-awesome/fa-check","info":"## Mattermost Command Validator\nValidates token, command, parameters and access.\n\n### Input object description\n**`Input` msg.payload**\n```\n{\n \"token\":\"\",\n \"channels\": { \n \"mode\":\"\", \n \"list\":[]\n },\n \"users\": { \n \"mode\":\"\", \n \"list\":[]\n },\n \"groups\": { \n \"mode\":\"\", \n \"list\":[]\n },\n \"validator\" : \"\",\n \"cmd_payload\": {}\n}\n```\n\n**token** - generated when creating cmd in Mattermost\n\n**channels.mode** - if set to \"white_list\" (default if no value set), channels.list array will represent allowed channels where command can be run, opposite if value equals \"black_list\"\n\n**users.mode** - if set to \"white_list\" (default if no value set), users.list array will represent allowed users which can execute cmd, opposite if value equals \"black_list\"\n\n**groups.mode (not supported)** - if set to \"white_list\" (default if no value set), groups.list array will represent allowed groups which can execute cmd, opposite if value equals \"black_list\"\n\n**validator** - regex for arguments validation, stops after first match\n\n**cmd_payload** - raw payload from Mattermost command\n\n**on_error** - set to \"throw\" (default if no value set) if you want throw an error or to \"pass\" if you want to pass it via msg.error . \n\n\n### Output object description\n**`Output` msg.payload**\n```\n{\n \"cmd_args\": \"\"\n}\n```\n\n**cmd_args** - args passed to the executed command which pass validation\n\n\n### Output throwed error object description\n**`Output on error` msg.error**\n```\n{\n \"error\": \"\"\n}\n```\n\n**error** - error description, schema depends from error source (ie. JSON Schema Validator or token validation)\n\n"},{"id":"723d6555.f31b9c","type":"debug","z":"8d38716.c512b9","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":1050,"y":1820,"wires":[]}]