master
c 153 lines 5.64 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "web_api.h"
4
5 void host_labels2json(RRDHOST *host, BUFFER *wb, const char *key) {
6 buffer_json_member_add_object(wb, key);
7 rrdlabels_to_buffer_json_members(host->rrdlabels, wb);
8 buffer_json_object_close(wb);
9 }
10
11 void web_client_ensure_proper_authorization(struct web_client *w) {
12 internal_fatal(w->user_auth.method != USER_AUTH_METHOD_NONE && !(w->user_auth.access & HTTP_ACCESS_SIGNED_ID),
13 "signed-in permission should be set, but is missing");
14
15 internal_fatal(w->user_auth.method == USER_AUTH_METHOD_NONE && (w->user_auth.access & HTTP_ACCESS_SIGNED_ID),
16 "signed-in permission is set, but it shouldn't");
17
18 #ifdef NETDATA_GOD_MODE
19 web_client_set_permissions(w, HTTP_ACCESS_ALL, HTTP_USER_ROLE_ADMIN, USER_AUTH_METHOD_GOD);
20 #else
21 if(w->user_auth.method == USER_AUTH_METHOD_NONE) {
22 web_client_set_permissions(
23 w,
24 (netdata_is_protected_by_bearer) ? HTTP_ACCESS_NONE : HTTP_ACCESS_ANONYMOUS_DATA,
25 (netdata_is_protected_by_bearer) ? HTTP_USER_ROLE_NONE : HTTP_USER_ROLE_ANY,
26 USER_AUTH_METHOD_NONE);
27 }
28 #endif
29 }
30
31 int web_client_api_request_vX(RRDHOST *host, struct web_client *w, char *url_path_endpoint, struct web_api_command *api_commands) {
32 buffer_no_cacheable(w->response.data);
33
34 web_client_ensure_proper_authorization(w);
35
36 if(unlikely(!url_path_endpoint || !*url_path_endpoint)) {
37 buffer_flush(w->response.data);
38 buffer_sprintf(w->response.data, "Which API command?");
39 return HTTP_RESP_BAD_REQUEST;
40 }
41
42 char *api_command = strchr(url_path_endpoint, '/');
43 if (likely(api_command == NULL)) // only config command supports subpaths for now
44 api_command = url_path_endpoint;
45 else {
46 size_t api_command_len = api_command - url_path_endpoint;
47 api_command = callocz(1, api_command_len + 1);
48 memcpy(api_command, url_path_endpoint, api_command_len);
49 }
50
51 uint32_t hash = simple_hash(api_command);
52
53 for(int i = 0; api_commands[i].api ; i++) {
54 if(unlikely(hash == api_commands[i].hash && !strcmp(api_command, api_commands[i].api))) {
55 if(unlikely(!api_commands[i].allow_subpaths && api_command != url_path_endpoint)) {
56 buffer_flush(w->response.data);
57 buffer_sprintf(w->response.data, "API command '%s' does not support subpaths.", api_command);
58 freez(api_command);
59 return HTTP_RESP_BAD_REQUEST;
60 }
61
62 if (api_command != url_path_endpoint)
63 freez(api_command);
64
65 bool acl_allows = ((w->acl & api_commands[i].acl) == api_commands[i].acl) || (api_commands[i].acl & HTTP_ACL_NOCHECK);
66 if(!acl_allows)
67 return web_client_permission_denied_acl(w);
68
69 bool permissions_allows =
70 http_access_user_has_enough_access_level_for_endpoint(w->user_auth.access, api_commands[i].access);
71 if(!permissions_allows)
72 return web_client_permission_denied(w);
73
74 char *query_string = (char *)buffer_tostring(w->url_query_string_decoded);
75
76 if(*query_string == '?')
77 query_string = &query_string[1];
78
79 return api_commands[i].callback(host, w, query_string);
80 }
81 }
82
83 if (api_command != url_path_endpoint)
84 freez(api_command);
85
86 buffer_flush(w->response.data);
87 buffer_strcat(w->response.data, "Unsupported API command: ");
88 buffer_strcat_htmlescape(w->response.data, url_path_endpoint);
89 return HTTP_RESP_NOT_FOUND;
90 }
91
92 RRDCONTEXT_TO_JSON_OPTIONS rrdcontext_to_json_parse_options(char *o) {
93 RRDCONTEXT_TO_JSON_OPTIONS options = RRDCONTEXT_OPTION_NONE;
94 char *tok;
95
96 while(o && *o && (tok = strsep_skip_consecutive_separators(&o, ", |"))) {
97 if(!*tok) continue;
98
99 if(!strcmp(tok, "full") || !strcmp(tok, "all"))
100 options |= RRDCONTEXT_OPTIONS_ALL;
101 else if(!strcmp(tok, "charts") || !strcmp(tok, "instances"))
102 options |= RRDCONTEXT_OPTION_SHOW_INSTANCES;
103 else if(!strcmp(tok, "dimensions") || !strcmp(tok, "metrics"))
104 options |= RRDCONTEXT_OPTION_SHOW_METRICS;
105 else if(!strcmp(tok, "queue"))
106 options |= RRDCONTEXT_OPTION_SHOW_QUEUED;
107 else if(!strcmp(tok, "flags"))
108 options |= RRDCONTEXT_OPTION_SHOW_FLAGS;
109 else if(!strcmp(tok, "uuids"))
110 options |= RRDCONTEXT_OPTION_SHOW_UUIDS;
111 else if(!strcmp(tok, "deleted"))
112 options |= RRDCONTEXT_OPTION_SHOW_DELETED;
113 else if(!strcmp(tok, "labels"))
114 options |= RRDCONTEXT_OPTION_SHOW_LABELS;
115 else if(!strcmp(tok, "deepscan"))
116 options |= RRDCONTEXT_OPTION_DEEPSCAN;
117 else if(!strcmp(tok, "hidden"))
118 options |= RRDCONTEXT_OPTION_SHOW_HIDDEN;
119 else if(!strcmp(tok, "rfc3339"))
120 options |= RRDCONTEXT_OPTION_RFC3339;
121 }
122
123 return options;
124 }
125
126
127 bool web_client_interrupt_callback(void *data) {
128 struct web_client *w = data;
129
130 bool ret;
131 if(w->interrupt.callback)
132 ret = w->interrupt.callback(w, w->interrupt.callback_data);
133 else
134 ret = is_socket_closed(w->fd);
135
136 return ret;
137 }
138
139 void nd_web_api_init(void) {
140 contexts_alert_statuses_init();
141 rrdr_options_init();
142 contexts_options_init();
143 datasource_formats_init();
144 time_grouping_init();
145 }
146
147 void web_client_progress_functions_update(nd_uuid_t *transaction, void *data, size_t done, size_t all) {
148 // handle progress updates from the plugin
149 // data parameter is no longer used - transaction is provided directly
150 (void)data;
151 query_progress_functions_update(transaction, done, all);
152 }
153