Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2021 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
12 :
13 : You should have received a copy of the GNU Affero General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file taler-exchange-httpd_management_extensions.c
18 : * @brief Handle request to POST /management/extensions
19 : * @author Özgür Kesim
20 : */
21 : #include "platform.h"
22 : #include <gnunet/gnunet_util_lib.h>
23 : #include <gnunet/gnunet_json_lib.h>
24 : #include <jansson.h>
25 : #include <microhttpd.h>
26 : #include "taler_json_lib.h"
27 : #include "taler_mhd_lib.h"
28 : #include "taler_signatures.h"
29 : #include "taler-exchange-httpd_management.h"
30 : #include "taler-exchange-httpd_responses.h"
31 : #include "taler_extensions.h"
32 : #include "taler_dbevents.h"
33 :
34 : /**
35 : * Extension carries the necessary data for a particular extension.
36 : *
37 : */
38 : struct Extension
39 : {
40 : enum TALER_Extension_Type type;
41 : json_t *config;
42 : };
43 :
44 : /**
45 : * Closure for the #set_extensions transaction
46 : */
47 : struct SetExtensionsContext
48 : {
49 : uint32_t num_extensions;
50 : struct Extension *extensions;
51 : struct TALER_MasterSignatureP extensions_sig;
52 : };
53 :
54 : /**
55 : * Function implementing database transaction to set the configuration of
56 : * extensions. It runs the transaction logic.
57 : * - IF it returns a non-error code, the transaction logic MUST NOT queue a
58 : * MHD response.
59 : * - IF it returns an hard error, the transaction logic MUST queue a MHD
60 : * response and set @a mhd_ret.
61 : * - IF it returns the soft error code, the function MAY be called again to
62 : * retry and MUST not queue a MHD response.
63 : *
64 : * @param cls closure with a `struct SetExtensionsContext`
65 : * @param connection MHD request which triggered the transaction
66 : * @param[out] mhd_ret set to MHD response status for @a connection,
67 : * if transaction failed (!)
68 : * @return transaction status
69 : */
70 : static enum GNUNET_DB_QueryStatus
71 0 : set_extensions (void *cls,
72 : struct MHD_Connection *connection,
73 : MHD_RESULT *mhd_ret)
74 : {
75 0 : struct SetExtensionsContext *sec = cls;
76 :
77 : /* save the configurations of all extensions */
78 0 : for (uint32_t i = 0; i<sec->num_extensions; i++)
79 : {
80 0 : struct Extension *ext = &sec->extensions[i];
81 : const struct TALER_Extension *taler_ext;
82 : enum GNUNET_DB_QueryStatus qs;
83 : char *config;
84 :
85 0 : taler_ext = TALER_extensions_get_by_type (ext->type);
86 0 : if (NULL == taler_ext)
87 : {
88 : /* No such extension found */
89 0 : GNUNET_break (0);
90 0 : return GNUNET_DB_STATUS_HARD_ERROR;
91 : }
92 :
93 0 : GNUNET_assert (NULL != ext->config);
94 :
95 0 : config = json_dumps (ext->config, JSON_COMPACT | JSON_SORT_KEYS);
96 0 : if (NULL == config)
97 : {
98 0 : GNUNET_break (0);
99 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
100 : MHD_HTTP_INTERNAL_SERVER_ERROR,
101 : TALER_EC_GENERIC_JSON_INVALID,
102 : "convert configuration to string");
103 0 : return GNUNET_DB_STATUS_HARD_ERROR;
104 : }
105 :
106 0 : qs = TEH_plugin->set_extension_config (
107 0 : TEH_plugin->cls,
108 0 : taler_ext->name,
109 : config);
110 :
111 0 : if (qs < 0)
112 : {
113 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
114 0 : return qs;
115 0 : GNUNET_break (0);
116 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
117 : MHD_HTTP_INTERNAL_SERVER_ERROR,
118 : TALER_EC_GENERIC_DB_STORE_FAILED,
119 : "save extension configuration");
120 : }
121 :
122 : /* Success, trigger event */
123 : {
124 0 : uint32_t nbo_type = htonl (sec->extensions[i].type);
125 0 : struct GNUNET_DB_EventHeaderP ev = {
126 0 : .size = htons (sizeof (ev)),
127 0 : .type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED)
128 : };
129 :
130 0 : TEH_plugin->event_notify (TEH_plugin->cls,
131 : &ev,
132 : &nbo_type,
133 : sizeof(nbo_type));
134 : }
135 :
136 : }
137 :
138 : /* All extensions configured, update the signature */
139 0 : TEH_extensions_sig = sec->extensions_sig;
140 :
141 0 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */
142 : }
143 :
144 :
145 : static enum GNUNET_GenericReturnValue
146 0 : verify_extensions_from_json (
147 : json_t *extensions,
148 : struct SetExtensionsContext *sec)
149 : {
150 : const char*name;
151 : const struct TALER_Extension *extension;
152 0 : size_t i = 0;
153 : json_t *blob;
154 :
155 0 : GNUNET_assert (NULL != extensions);
156 0 : GNUNET_assert (json_is_object (extensions));
157 :
158 0 : sec->num_extensions = json_object_size (extensions);
159 0 : sec->extensions = GNUNET_new_array (sec->num_extensions,
160 : struct Extension);
161 :
162 0 : json_object_foreach (extensions, name, blob)
163 : {
164 0 : int critical = 0;
165 : json_t *config;
166 0 : const char *version = NULL;
167 :
168 : /* load and verify criticality, version, etc. */
169 0 : extension = TALER_extensions_get_by_name (name);
170 0 : if (NULL == extension)
171 : {
172 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
173 : "no such extension: %s\n", name);
174 0 : return GNUNET_SYSERR;
175 : }
176 :
177 0 : if (GNUNET_OK !=
178 0 : TALER_extensions_is_json_config (
179 : blob, &critical, &version, &config))
180 0 : return GNUNET_SYSERR;
181 :
182 0 : if (critical != extension->critical
183 0 : || 0 != strcmp (version, extension->version) // FIXME-oec: libtool compare
184 0 : || NULL == config
185 0 : || GNUNET_OK != extension->test_json_config (config))
186 0 : return GNUNET_SYSERR;
187 :
188 0 : sec->extensions[i].type = extension->type;
189 0 : sec->extensions[i].config = config;
190 : }
191 :
192 0 : return GNUNET_OK;
193 : }
194 :
195 :
196 : MHD_RESULT
197 0 : TEH_handler_management_post_extensions (
198 : struct MHD_Connection *connection,
199 : const json_t *root)
200 : {
201 : MHD_RESULT ret;
202 : json_t *extensions;
203 0 : struct SetExtensionsContext sec = {0};
204 : struct GNUNET_JSON_Specification top_spec[] = {
205 0 : GNUNET_JSON_spec_json ("extensions",
206 : &extensions),
207 0 : GNUNET_JSON_spec_fixed_auto ("extensions_sig",
208 : &sec.extensions_sig),
209 0 : GNUNET_JSON_spec_end ()
210 : };
211 :
212 : /* Parse the top level json structure */
213 : {
214 : enum GNUNET_GenericReturnValue res;
215 :
216 0 : res = TALER_MHD_parse_json_data (connection,
217 : root,
218 : top_spec);
219 0 : if (GNUNET_SYSERR == res)
220 0 : return MHD_NO; /* hard failure */
221 0 : if (GNUNET_NO == res)
222 0 : return MHD_YES; /* failure */
223 : }
224 :
225 : /* Ensure we have an object */
226 0 : if (! json_is_object (extensions))
227 : {
228 0 : GNUNET_JSON_parse_free (top_spec);
229 0 : return TALER_MHD_reply_with_error (
230 : connection,
231 : MHD_HTTP_BAD_REQUEST,
232 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
233 : "invalid object");
234 : }
235 :
236 : /* Verify the signature */
237 : {
238 : struct TALER_ExtensionConfigHashP h_config;
239 :
240 0 : if (GNUNET_OK !=
241 0 : TALER_JSON_extensions_config_hash (extensions, &h_config) ||
242 : GNUNET_OK !=
243 0 : TALER_exchange_offline_extension_config_hash_verify (
244 : &h_config,
245 : &TEH_master_public_key,
246 : &sec.extensions_sig))
247 : {
248 0 : GNUNET_JSON_parse_free (top_spec);
249 0 : return TALER_MHD_reply_with_error (
250 : connection,
251 : MHD_HTTP_BAD_REQUEST,
252 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
253 : "invalid signuture");
254 : }
255 : }
256 :
257 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
258 : "Received /management/extensions\n");
259 :
260 : /* Now parse individual extensions and signatures from those objects. */
261 0 : if (GNUNET_OK !=
262 0 : verify_extensions_from_json (extensions, &sec))
263 : {
264 0 : GNUNET_JSON_parse_free (top_spec);
265 0 : return TALER_MHD_reply_with_error (
266 : connection,
267 : MHD_HTTP_BAD_REQUEST,
268 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
269 : "invalid object");
270 : }
271 :
272 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
273 : "Received %u extensions\n",
274 : sec.num_extensions);
275 :
276 : /* now run the transaction to persist the configurations */
277 : {
278 : enum GNUNET_GenericReturnValue res;
279 :
280 0 : res = TEH_DB_run_transaction (connection,
281 : "set extensions",
282 : TEH_MT_REQUEST_OTHER,
283 : &ret,
284 : &set_extensions,
285 : &sec);
286 :
287 0 : if (GNUNET_SYSERR == res)
288 0 : goto CLEANUP;
289 : }
290 :
291 0 : ret = TALER_MHD_reply_static (
292 : connection,
293 : MHD_HTTP_NO_CONTENT,
294 : NULL,
295 : NULL,
296 : 0);
297 :
298 0 : CLEANUP:
299 0 : for (unsigned int i = 0; i < sec.num_extensions; i++)
300 : {
301 0 : if (NULL != sec.extensions[i].config)
302 : {
303 0 : json_decref (sec.extensions[i].config);
304 : }
305 : }
306 0 : GNUNET_free (sec.extensions);
307 0 : GNUNET_JSON_parse_free (top_spec);
308 0 : return ret;
309 : }
310 :
311 :
312 : /* end of taler-exchange-httpd_management_management_post_extensions.c */
|