web_api.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "web_api.h"
  3. bool netdata_is_protected_by_bearer = false; // this is controlled by cloud, at the point the agent logs in - this should also be saved to /var/lib/netdata
  4. DICTIONARY *netdata_authorized_bearers = NULL;
  5. static bool web_client_check_acl_and_bearer(struct web_client *w, WEB_CLIENT_ACL endpoint_acl) {
  6. if(endpoint_acl == WEB_CLIENT_ACL_NOCHECK)
  7. // the endpoint is totally public
  8. return true;
  9. bool acl_allows = w->acl & endpoint_acl;
  10. if(!acl_allows)
  11. // the channel we received the request from (w->acl) is not compatible with the endpoint
  12. return false;
  13. if(!netdata_is_protected_by_bearer && !(endpoint_acl & WEB_CLIENT_ACL_BEARER_REQUIRED))
  14. // bearer protection is not enabled and is not required by the endpoint
  15. return true;
  16. if(!(endpoint_acl & (WEB_CLIENT_ACL_BEARER_REQUIRED|WEB_CLIENT_ACL_BEARER_OPTIONAL)))
  17. // endpoint does not require a bearer
  18. return true;
  19. if((w->acl & (WEB_CLIENT_ACL_ACLK|WEB_CLIENT_ACL_WEBRTC)) || api_check_bearer_token(w))
  20. // the request is coming from ACLK or WEBRTC (authorized already),
  21. // or we have a valid bearer on the request
  22. return true;
  23. return false;
  24. }
  25. int web_client_api_request_vX(RRDHOST *host, struct web_client *w, char *url_path_endpoint, struct web_api_command *api_commands) {
  26. if(unlikely(!url_path_endpoint || !*url_path_endpoint)) {
  27. buffer_flush(w->response.data);
  28. buffer_sprintf(w->response.data, "Which API command?");
  29. return HTTP_RESP_BAD_REQUEST;
  30. }
  31. uint32_t hash = simple_hash(url_path_endpoint);
  32. for(int i = 0; api_commands[i].command ; i++) {
  33. if(unlikely(hash == api_commands[i].hash && !strcmp(url_path_endpoint, api_commands[i].command))) {
  34. if(unlikely(!web_client_check_acl_and_bearer(w, api_commands[i].acl)))
  35. return web_client_bearer_required(w);
  36. char *query_string = (char *)buffer_tostring(w->url_query_string_decoded);
  37. if(*query_string == '?')
  38. query_string = &query_string[1];
  39. return api_commands[i].callback(host, w, query_string);
  40. }
  41. }
  42. buffer_flush(w->response.data);
  43. buffer_strcat(w->response.data, "Unsupported API command: ");
  44. buffer_strcat_htmlescape(w->response.data, url_path_endpoint);
  45. return HTTP_RESP_NOT_FOUND;
  46. }
  47. RRDCONTEXT_TO_JSON_OPTIONS rrdcontext_to_json_parse_options(char *o) {
  48. RRDCONTEXT_TO_JSON_OPTIONS options = RRDCONTEXT_OPTION_NONE;
  49. char *tok;
  50. while(o && *o && (tok = strsep_skip_consecutive_separators(&o, ", |"))) {
  51. if(!*tok) continue;
  52. if(!strcmp(tok, "full") || !strcmp(tok, "all"))
  53. options |= RRDCONTEXT_OPTIONS_ALL;
  54. else if(!strcmp(tok, "charts") || !strcmp(tok, "instances"))
  55. options |= RRDCONTEXT_OPTION_SHOW_INSTANCES;
  56. else if(!strcmp(tok, "dimensions") || !strcmp(tok, "metrics"))
  57. options |= RRDCONTEXT_OPTION_SHOW_METRICS;
  58. else if(!strcmp(tok, "queue"))
  59. options |= RRDCONTEXT_OPTION_SHOW_QUEUED;
  60. else if(!strcmp(tok, "flags"))
  61. options |= RRDCONTEXT_OPTION_SHOW_FLAGS;
  62. else if(!strcmp(tok, "uuids"))
  63. options |= RRDCONTEXT_OPTION_SHOW_UUIDS;
  64. else if(!strcmp(tok, "deleted"))
  65. options |= RRDCONTEXT_OPTION_SHOW_DELETED;
  66. else if(!strcmp(tok, "labels"))
  67. options |= RRDCONTEXT_OPTION_SHOW_LABELS;
  68. else if(!strcmp(tok, "deepscan"))
  69. options |= RRDCONTEXT_OPTION_DEEPSCAN;
  70. else if(!strcmp(tok, "hidden"))
  71. options |= RRDCONTEXT_OPTION_SHOW_HIDDEN;
  72. }
  73. return options;
  74. }
  75. int web_client_api_request_weights(RRDHOST *host, struct web_client *w, char *url, WEIGHTS_METHOD method, WEIGHTS_FORMAT format, size_t api_version) {
  76. if (!netdata_ready)
  77. return HTTP_RESP_BACKEND_FETCH_FAILED;
  78. time_t baseline_after = 0, baseline_before = 0, after = 0, before = 0;
  79. size_t points = 0;
  80. RRDR_OPTIONS options = 0;
  81. RRDR_TIME_GROUPING time_group_method = RRDR_GROUPING_AVERAGE;
  82. time_t timeout_ms = 0;
  83. size_t tier = 0;
  84. const char *time_group_options = NULL, *scope_contexts = NULL, *scope_nodes = NULL, *contexts = NULL, *nodes = NULL,
  85. *instances = NULL, *dimensions = NULL, *labels = NULL, *alerts = NULL;
  86. struct group_by_pass group_by = {
  87. .group_by = RRDR_GROUP_BY_NONE,
  88. .group_by_label = NULL,
  89. .aggregation = RRDR_GROUP_BY_FUNCTION_AVERAGE,
  90. };
  91. while (url) {
  92. char *value = strsep_skip_consecutive_separators(&url, "&");
  93. if (!value || !*value)
  94. continue;
  95. char *name = strsep_skip_consecutive_separators(&value, "=");
  96. if (!name || !*name)
  97. continue;
  98. if (!value || !*value)
  99. continue;
  100. if (!strcmp(name, "baseline_after"))
  101. baseline_after = str2l(value);
  102. else if (!strcmp(name, "baseline_before"))
  103. baseline_before = str2l(value);
  104. else if (!strcmp(name, "after") || !strcmp(name, "highlight_after"))
  105. after = str2l(value);
  106. else if (!strcmp(name, "before") || !strcmp(name, "highlight_before"))
  107. before = str2l(value);
  108. else if (!strcmp(name, "points") || !strcmp(name, "max_points"))
  109. points = str2ul(value);
  110. else if (!strcmp(name, "timeout"))
  111. timeout_ms = str2l(value);
  112. else if((api_version == 1 && !strcmp(name, "group")) || (api_version >= 2 && !strcmp(name, "time_group")))
  113. time_group_method = time_grouping_parse(value, RRDR_GROUPING_AVERAGE);
  114. else if((api_version == 1 && !strcmp(name, "group_options")) || (api_version >= 2 && !strcmp(name, "time_group_options")))
  115. time_group_options = value;
  116. else if(!strcmp(name, "options"))
  117. options |= web_client_api_request_v1_data_options(value);
  118. else if(!strcmp(name, "method"))
  119. method = weights_string_to_method(value);
  120. else if(api_version == 1 && (!strcmp(name, "context") || !strcmp(name, "contexts")))
  121. scope_contexts = value;
  122. else if(api_version >= 2 && !strcmp(name, "scope_nodes")) scope_nodes = value;
  123. else if(api_version >= 2 && !strcmp(name, "scope_contexts")) scope_contexts = value;
  124. else if(api_version >= 2 && !strcmp(name, "nodes")) nodes = value;
  125. else if(api_version >= 2 && !strcmp(name, "contexts")) contexts = value;
  126. else if(api_version >= 2 && !strcmp(name, "instances")) instances = value;
  127. else if(api_version >= 2 && !strcmp(name, "dimensions")) dimensions = value;
  128. else if(api_version >= 2 && !strcmp(name, "labels")) labels = value;
  129. else if(api_version >= 2 && !strcmp(name, "alerts")) alerts = value;
  130. else if(api_version >= 2 && (!strcmp(name, "group_by") || !strcmp(name, "group_by[0]"))) {
  131. group_by.group_by = group_by_parse(value);
  132. }
  133. else if(api_version >= 2 && (!strcmp(name, "group_by_label") || !strcmp(name, "group_by_label[0]"))) {
  134. group_by.group_by_label = value;
  135. }
  136. else if(api_version >= 2 && (!strcmp(name, "aggregation") || !strcmp(name, "aggregation[0]"))) {
  137. group_by.aggregation = group_by_aggregate_function_parse(value);
  138. }
  139. else if(!strcmp(name, "tier")) {
  140. tier = str2ul(value);
  141. if(tier < storage_tiers)
  142. options |= RRDR_OPTION_SELECTED_TIER;
  143. else
  144. tier = 0;
  145. }
  146. }
  147. if(options == 0)
  148. // the user did not set any options
  149. options = RRDR_OPTION_NOT_ALIGNED | RRDR_OPTION_NULL2ZERO | RRDR_OPTION_NONZERO;
  150. else
  151. // the user set some options, add also these
  152. options |= RRDR_OPTION_NOT_ALIGNED | RRDR_OPTION_NULL2ZERO;
  153. if(options & RRDR_OPTION_PERCENTAGE)
  154. options |= RRDR_OPTION_ABSOLUTE;
  155. if(options & RRDR_OPTION_DEBUG)
  156. options &= ~RRDR_OPTION_MINIFY;
  157. BUFFER *wb = w->response.data;
  158. buffer_flush(wb);
  159. wb->content_type = CT_APPLICATION_JSON;
  160. QUERY_WEIGHTS_REQUEST qwr = {
  161. .version = api_version,
  162. .host = (api_version == 1) ? NULL : host,
  163. .scope_nodes = scope_nodes,
  164. .scope_contexts = scope_contexts,
  165. .nodes = nodes,
  166. .contexts = contexts,
  167. .instances = instances,
  168. .dimensions = dimensions,
  169. .labels = labels,
  170. .alerts = alerts,
  171. .group_by = {
  172. .group_by = group_by.group_by,
  173. .group_by_label = group_by.group_by_label,
  174. .aggregation = group_by.aggregation,
  175. },
  176. .method = method,
  177. .format = format,
  178. .time_group_method = time_group_method,
  179. .time_group_options = time_group_options,
  180. .baseline_after = baseline_after,
  181. .baseline_before = baseline_before,
  182. .after = after,
  183. .before = before,
  184. .points = points,
  185. .options = options,
  186. .tier = tier,
  187. .timeout_ms = timeout_ms,
  188. .interrupt_callback = web_client_interrupt_callback,
  189. .interrupt_callback_data = w,
  190. };
  191. return web_api_v12_weights(wb, &qwr);
  192. }
  193. bool web_client_interrupt_callback(void *data) {
  194. struct web_client *w = data;
  195. if(w->interrupt.callback)
  196. return w->interrupt.callback(w, w->interrupt.callback_data);
  197. return sock_has_output_error(w->ofd);
  198. }