Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2020-2025 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Affero General Public License as
7 : published by the Free Software Foundation; either version 3,
8 : or (at your option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not,
17 : see <http://www.gnu.org/licenses/>
18 : */
19 :
20 : /**
21 : * @file src/backend/taler-merchant-httpd_post-management-instances.c
22 : * @brief implementing POST /instances request handling
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include "taler-merchant-httpd_post-management-instances.h"
27 : #include "taler-merchant-httpd_helper.h"
28 : #include "taler-merchant-httpd.h"
29 : #include "taler-merchant-httpd_auth.h"
30 : #include "taler-merchant-httpd_mfa.h"
31 : #include "taler/taler_merchant_bank_lib.h"
32 : #include <taler/taler_dbevents.h>
33 : #include <taler/taler_json_lib.h>
34 : #include <regex.h>
35 : #include "merchant-database/insert_instance.h"
36 : #include "merchant-database/set_instance.h"
37 : #include "merchant-database/insert_login_token.h"
38 : #include "merchant-database/start.h"
39 :
40 : /**
41 : * How often do we retry the simple INSERT database transaction?
42 : */
43 : #define MAX_RETRIES 3
44 :
45 :
46 : /**
47 : * Generate an instance, given its configuration.
48 : *
49 : * @param rh context of the handler
50 : * @param connection the MHD connection to handle
51 : * @param[in,out] hc context with further information about the request
52 : * @param login_token_expiration set to how long a login token validity
53 : * should be, use zero if no login token should be created
54 : * @param validation_needed true if self-provisioned and
55 : * email/phone registration is required before the
56 : * instance can become fully active
57 : * @return MHD result code
58 : */
59 : static enum MHD_Result
60 42 : post_instances (const struct TMH_RequestHandler *rh,
61 : struct MHD_Connection *connection,
62 : struct TMH_HandlerContext *hc,
63 : struct GNUNET_TIME_Relative login_token_expiration,
64 : bool validation_needed)
65 : {
66 42 : struct TALER_MERCHANTDB_InstanceSettings is = { 0 };
67 : struct TALER_MERCHANTDB_InstanceAuthSettings ias;
68 42 : const char *auth_password = NULL;
69 42 : struct TMH_WireMethod *wm_head = NULL;
70 42 : struct TMH_WireMethod *wm_tail = NULL;
71 : const json_t *jauth;
72 42 : const char *iphone = NULL;
73 : char *id_lower;
74 : bool no_pay_delay;
75 : bool no_refund_delay;
76 : bool no_transfer_delay;
77 : bool no_rounding_interval;
78 : struct GNUNET_JSON_Specification spec[] = {
79 42 : TALER_JSON_spec_slug ("id",
80 : (const char **) &is.id),
81 42 : GNUNET_JSON_spec_string ("name",
82 : (const char **) &is.name),
83 42 : GNUNET_JSON_spec_mark_optional (
84 : GNUNET_JSON_spec_string ("email",
85 : (const char **) &is.email),
86 : NULL),
87 42 : GNUNET_JSON_spec_mark_optional (
88 : GNUNET_JSON_spec_string ("phone_number",
89 : &iphone),
90 : NULL),
91 42 : GNUNET_JSON_spec_mark_optional (
92 : TALER_JSON_spec_web_url ("website",
93 : (const char **) &is.website),
94 : NULL),
95 42 : GNUNET_JSON_spec_mark_optional (
96 : GNUNET_JSON_spec_string ("logo",
97 : (const char **) &is.logo),
98 : NULL),
99 42 : GNUNET_JSON_spec_object_const ("auth",
100 : &jauth),
101 42 : GNUNET_JSON_spec_json ("address",
102 : &is.address),
103 42 : GNUNET_JSON_spec_json ("jurisdiction",
104 : &is.jurisdiction),
105 42 : GNUNET_JSON_spec_bool ("use_stefan",
106 : &is.use_stefan),
107 42 : GNUNET_JSON_spec_mark_optional (
108 : GNUNET_JSON_spec_relative_time ("default_pay_delay",
109 : &is.default_pay_delay),
110 : &no_pay_delay),
111 42 : GNUNET_JSON_spec_mark_optional (
112 : GNUNET_JSON_spec_relative_time ("default_refund_delay",
113 : &is.default_refund_delay),
114 : &no_refund_delay),
115 42 : GNUNET_JSON_spec_mark_optional (
116 : GNUNET_JSON_spec_relative_time ("default_wire_transfer_delay",
117 : &is.default_wire_transfer_delay),
118 : &no_transfer_delay),
119 42 : GNUNET_JSON_spec_mark_optional (
120 : GNUNET_JSON_spec_time_rounder_interval (
121 : "default_wire_transfer_rounding_interval",
122 : &is.default_wire_transfer_rounding_interval),
123 : &no_rounding_interval),
124 42 : GNUNET_JSON_spec_end ()
125 : };
126 :
127 : {
128 : enum GNUNET_GenericReturnValue res;
129 :
130 42 : res = TALER_MHD_parse_json_data (connection,
131 42 : hc->request_body,
132 : spec);
133 42 : if (GNUNET_OK != res)
134 : return (GNUNET_NO == res)
135 : ? MHD_YES
136 0 : : MHD_NO;
137 : }
138 42 : if (no_pay_delay)
139 0 : is.default_pay_delay = TMH_default_pay_delay;
140 42 : if (no_refund_delay)
141 23 : is.default_refund_delay = TMH_default_refund_delay;
142 42 : if (no_transfer_delay)
143 0 : is.default_wire_transfer_delay = TMH_default_wire_transfer_delay;
144 42 : if (no_rounding_interval)
145 : is.default_wire_transfer_rounding_interval
146 42 : = TMH_default_wire_transfer_rounding_interval;
147 42 : if (GNUNET_TIME_relative_is_forever (is.default_pay_delay))
148 : {
149 0 : GNUNET_break_op (0);
150 0 : GNUNET_JSON_parse_free (spec);
151 0 : return TALER_MHD_reply_with_error (connection,
152 : MHD_HTTP_BAD_REQUEST,
153 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
154 : "default_pay_delay");
155 : }
156 42 : if (GNUNET_TIME_relative_is_forever (is.default_refund_delay))
157 : {
158 0 : GNUNET_break_op (0);
159 0 : GNUNET_JSON_parse_free (spec);
160 0 : return TALER_MHD_reply_with_error (connection,
161 : MHD_HTTP_BAD_REQUEST,
162 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
163 : "default_refund_delay");
164 : }
165 42 : if (GNUNET_TIME_relative_is_forever (is.default_wire_transfer_delay))
166 : {
167 0 : GNUNET_break_op (0);
168 0 : GNUNET_JSON_parse_free (spec);
169 0 : return TALER_MHD_reply_with_error (connection,
170 : MHD_HTTP_BAD_REQUEST,
171 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
172 : "default_wire_transfer_delay");
173 : }
174 42 : if (NULL != iphone)
175 : {
176 5 : is.phone = TALER_MERCHANT_phone_validate_normalize (iphone,
177 : false);
178 5 : if (NULL == is.phone)
179 : {
180 0 : GNUNET_break_op (0);
181 0 : GNUNET_JSON_parse_free (spec);
182 0 : return TALER_MHD_reply_with_error (connection,
183 : MHD_HTTP_BAD_REQUEST,
184 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
185 : "phone_number");
186 : }
187 5 : if ( (NULL != TMH_phone_regex) &&
188 : (0 !=
189 0 : regexec (&TMH_phone_rx,
190 0 : is.phone,
191 : 0,
192 : NULL,
193 : 0)) )
194 : {
195 0 : GNUNET_break_op (0);
196 0 : GNUNET_JSON_parse_free (spec);
197 0 : return TALER_MHD_reply_with_error (connection,
198 : MHD_HTTP_BAD_REQUEST,
199 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
200 : "phone_number");
201 : }
202 : }
203 42 : if ( (NULL != is.email) &&
204 5 : (! TALER_MERCHANT_email_valid (is.email)) )
205 : {
206 0 : GNUNET_break_op (0);
207 0 : GNUNET_JSON_parse_free (spec);
208 0 : GNUNET_free (is.phone);
209 0 : return TALER_MHD_reply_with_error (connection,
210 : MHD_HTTP_BAD_REQUEST,
211 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
212 : "email");
213 : }
214 :
215 : {
216 : enum GNUNET_GenericReturnValue ret;
217 :
218 42 : ret = TMH_check_auth_config (connection,
219 : jauth,
220 : &auth_password);
221 42 : if (GNUNET_OK != ret)
222 : {
223 0 : GNUNET_free (is.phone);
224 0 : GNUNET_JSON_parse_free (spec);
225 0 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
226 : }
227 : }
228 :
229 : /* check 'id' well-formed */
230 : {
231 : static bool once;
232 : static regex_t reg;
233 42 : bool id_wellformed = true;
234 :
235 42 : if (! once)
236 : {
237 19 : once = true;
238 19 : GNUNET_assert (0 ==
239 : regcomp (®,
240 : "^[A-Za-z0-9][A-Za-z0-9_.@-]+$",
241 : REG_EXTENDED));
242 : }
243 :
244 42 : if (0 != regexec (®,
245 42 : is.id,
246 : 0, NULL, 0))
247 0 : id_wellformed = false;
248 42 : if (! id_wellformed)
249 : {
250 0 : GNUNET_JSON_parse_free (spec);
251 0 : GNUNET_free (is.phone);
252 0 : return TALER_MHD_reply_with_error (connection,
253 : MHD_HTTP_BAD_REQUEST,
254 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
255 : "id");
256 : }
257 : }
258 :
259 42 : if (! TMH_location_object_valid (is.address))
260 : {
261 0 : GNUNET_break_op (0);
262 0 : GNUNET_JSON_parse_free (spec);
263 0 : GNUNET_free (is.phone);
264 0 : return TALER_MHD_reply_with_error (connection,
265 : MHD_HTTP_BAD_REQUEST,
266 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
267 : "address");
268 : }
269 :
270 42 : if (! TMH_location_object_valid (is.jurisdiction))
271 : {
272 0 : GNUNET_break_op (0);
273 0 : GNUNET_JSON_parse_free (spec);
274 0 : GNUNET_free (is.phone);
275 0 : return TALER_MHD_reply_with_error (connection,
276 : MHD_HTTP_BAD_REQUEST,
277 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
278 : "jurisdiction");
279 : }
280 :
281 42 : if ( (NULL != is.logo) &&
282 0 : (! TALER_MERCHANT_image_data_url_valid (is.logo)) )
283 : {
284 0 : GNUNET_break_op (0);
285 0 : GNUNET_JSON_parse_free (spec);
286 0 : GNUNET_free (is.phone);
287 0 : return TALER_MHD_reply_with_error (connection,
288 : MHD_HTTP_BAD_REQUEST,
289 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
290 : "logo");
291 : }
292 :
293 42 : id_lower = GNUNET_STRINGS_utf8_tolower (is.id);
294 : {
295 : /* Test if an instance of this id is known */
296 : struct TMH_MerchantInstance *mi;
297 :
298 42 : mi = TMH_lookup_instance (id_lower);
299 42 : if (NULL != mi)
300 : {
301 : enum MHD_Result ret;
302 :
303 2 : if (mi->deleted)
304 : {
305 0 : GNUNET_JSON_parse_free (spec);
306 0 : GNUNET_free (is.phone);
307 0 : GNUNET_free (id_lower);
308 0 : return TALER_MHD_reply_with_error (
309 : connection,
310 : MHD_HTTP_CONFLICT,
311 : TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_PURGE_REQUIRED,
312 0 : is.id);
313 : }
314 : /* Check for idempotency */
315 2 : if ( (0 == strcmp (mi->settings.id,
316 2 : id_lower)) &&
317 2 : (0 == strcmp (mi->settings.name,
318 2 : is.name)) &&
319 2 : ((mi->settings.email == is.email) ||
320 0 : (NULL != is.email && NULL != mi->settings.email &&
321 0 : 0 == strcmp (mi->settings.email,
322 0 : is.email))) &&
323 2 : ((mi->settings.website == is.website) ||
324 0 : (NULL != is.website && NULL != mi->settings.website &&
325 0 : 0 == strcmp (mi->settings.website,
326 0 : is.website))) &&
327 2 : ((mi->settings.logo == is.logo) ||
328 0 : (NULL != is.logo && NULL != mi->settings.logo &&
329 0 : 0 == strcmp (mi->settings.logo,
330 0 : is.logo))) &&
331 2 : ( ( (NULL != auth_password) &&
332 : (GNUNET_OK ==
333 0 : TMH_check_auth (auth_password,
334 : &mi->auth.auth_salt,
335 2 : &mi->auth.auth_hash)) ) ||
336 4 : ( (NULL == auth_password) &&
337 : (GNUNET_YES ==
338 4 : GNUNET_is_zero (&mi->auth.auth_hash))) ) &&
339 2 : (1 == json_equal (mi->settings.address,
340 4 : is.address)) &&
341 2 : (1 == json_equal (mi->settings.jurisdiction,
342 2 : is.jurisdiction)) &&
343 2 : (mi->settings.use_stefan == is.use_stefan) &&
344 2 : (GNUNET_TIME_relative_cmp (mi->settings.default_wire_transfer_delay,
345 : ==,
346 2 : is.default_wire_transfer_delay)) &&
347 2 : (GNUNET_TIME_relative_cmp (mi->settings.default_pay_delay,
348 : ==,
349 2 : is.default_pay_delay)) &&
350 2 : (GNUNET_TIME_relative_cmp (mi->settings.default_refund_delay,
351 : ==,
352 : is.default_refund_delay)) )
353 : {
354 2 : GNUNET_JSON_parse_free (spec);
355 2 : GNUNET_free (is.phone);
356 2 : GNUNET_free (id_lower);
357 2 : return TALER_MHD_reply_static (connection,
358 : MHD_HTTP_NO_CONTENT,
359 : NULL,
360 : NULL,
361 : 0);
362 : }
363 0 : GNUNET_JSON_parse_free (spec);
364 0 : GNUNET_free (is.phone);
365 0 : ret = TALER_MHD_reply_with_error (connection,
366 : MHD_HTTP_CONFLICT,
367 : TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS,
368 : id_lower);
369 0 : GNUNET_free (id_lower);
370 0 : return ret;
371 : }
372 : }
373 :
374 : /* Check MFA is satisfied */
375 40 : if (validation_needed)
376 : {
377 5 : enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
378 :
379 5 : if ( (0 != (TMH_TCS_SMS & TEH_mandatory_tan_channels)) &&
380 5 : (NULL == is.phone) )
381 : {
382 0 : GNUNET_break_op (0);
383 0 : GNUNET_JSON_parse_free (spec);
384 0 : GNUNET_free (is.phone); /* does nothing... */
385 0 : GNUNET_free (id_lower);
386 0 : return TALER_MHD_reply_with_error (connection,
387 : MHD_HTTP_BAD_REQUEST,
388 : TALER_EC_GENERIC_PARAMETER_MISSING,
389 : "phone_number");
390 :
391 : }
392 5 : if ( (0 != (TMH_TCS_EMAIL & TEH_mandatory_tan_channels)) &&
393 5 : (NULL == is.email) )
394 : {
395 0 : GNUNET_break_op (0);
396 0 : GNUNET_JSON_parse_free (spec);
397 0 : GNUNET_free (is.phone);
398 0 : GNUNET_free (id_lower);
399 0 : return TALER_MHD_reply_with_error (connection,
400 : MHD_HTTP_BAD_REQUEST,
401 : TALER_EC_GENERIC_PARAMETER_MISSING,
402 : "email");
403 : }
404 5 : switch (TEH_mandatory_tan_channels)
405 : {
406 0 : case TMH_TCS_NONE:
407 0 : GNUNET_assert (0);
408 : ret = GNUNET_OK;
409 : break;
410 0 : case TMH_TCS_SMS:
411 0 : is.phone_validated = true;
412 0 : ret = TMH_mfa_challenges_do (hc,
413 : id_lower,
414 : TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,
415 : true,
416 : TALER_MERCHANT_MFA_CHANNEL_SMS,
417 : is.phone,
418 : TALER_MERCHANT_MFA_CHANNEL_NONE);
419 0 : break;
420 0 : case TMH_TCS_EMAIL:
421 0 : is.email_validated = true;
422 0 : ret = TMH_mfa_challenges_do (hc,
423 : id_lower,
424 : TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,
425 : true,
426 : TALER_MERCHANT_MFA_CHANNEL_EMAIL,
427 : is.email,
428 : TALER_MERCHANT_MFA_CHANNEL_NONE);
429 0 : break;
430 5 : case TMH_TCS_EMAIL_AND_SMS:
431 5 : is.phone_validated = true;
432 5 : is.email_validated = true;
433 5 : ret = TMH_mfa_challenges_do (hc,
434 : id_lower,
435 : TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,
436 : true,
437 : TALER_MERCHANT_MFA_CHANNEL_EMAIL,
438 : is.email,
439 : TALER_MERCHANT_MFA_CHANNEL_SMS,
440 : is.phone,
441 : TALER_MERCHANT_MFA_CHANNEL_NONE);
442 5 : break;
443 : }
444 5 : if (GNUNET_OK != ret)
445 : {
446 3 : GNUNET_JSON_parse_free (spec);
447 3 : GNUNET_free (is.phone);
448 3 : GNUNET_free (id_lower);
449 : return (GNUNET_NO == ret)
450 : ? MHD_YES
451 3 : : MHD_NO;
452 : }
453 : }
454 :
455 : /* handle authentication token setup */
456 37 : if (NULL == auth_password)
457 : {
458 29 : memset (&ias.auth_salt,
459 : 0,
460 : sizeof (ias.auth_salt));
461 29 : memset (&ias.auth_hash,
462 : 0,
463 : sizeof (ias.auth_hash));
464 : }
465 : else
466 : {
467 : /* Sets 'auth_salt' and 'auth_hash' */
468 8 : TMH_compute_auth (auth_password,
469 : &ias.auth_salt,
470 : &ias.auth_hash);
471 : }
472 :
473 : /* create in-memory data structure */
474 : {
475 : struct TMH_MerchantInstance *mi;
476 : enum GNUNET_DB_QueryStatus qs;
477 :
478 37 : mi = GNUNET_new (struct TMH_MerchantInstance);
479 37 : mi->wm_head = wm_head;
480 37 : mi->wm_tail = wm_tail;
481 37 : mi->settings = is;
482 37 : mi->settings.address = json_incref (mi->settings.address);
483 37 : mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
484 37 : mi->settings.id = id_lower;
485 37 : mi->settings.name = GNUNET_strdup (is.name);
486 37 : if (NULL != is.email)
487 2 : mi->settings.email = GNUNET_strdup (is.email);
488 37 : mi->settings.phone = is.phone;
489 37 : is.phone = NULL;
490 37 : if (NULL != is.website)
491 0 : mi->settings.website = GNUNET_strdup (is.website);
492 37 : if (NULL != is.logo)
493 0 : mi->settings.logo = GNUNET_strdup (is.logo);
494 37 : mi->auth = ias;
495 37 : GNUNET_CRYPTO_eddsa_key_create (&mi->merchant_priv.eddsa_priv);
496 37 : GNUNET_CRYPTO_eddsa_key_get_public (&mi->merchant_priv.eddsa_priv,
497 : &mi->merchant_pub.eddsa_pub);
498 :
499 37 : for (unsigned int i = 0; i<MAX_RETRIES; i++)
500 : {
501 37 : if (GNUNET_OK !=
502 37 : TALER_MERCHANTDB_start (TMH_db,
503 : "post /instances"))
504 : {
505 0 : GNUNET_break (0);
506 0 : mi->rc = 1;
507 0 : TMH_instance_decref (mi);
508 0 : GNUNET_JSON_parse_free (spec);
509 0 : return TALER_MHD_reply_with_error (
510 : connection,
511 : MHD_HTTP_INTERNAL_SERVER_ERROR,
512 : TALER_EC_GENERIC_DB_START_FAILED,
513 : NULL);
514 : }
515 37 : qs = TALER_MERCHANTDB_insert_instance (TMH_db,
516 37 : &mi->merchant_pub,
517 37 : &mi->merchant_priv,
518 37 : &mi->settings,
519 37 : &mi->auth);
520 37 : switch (qs)
521 : {
522 0 : case GNUNET_DB_STATUS_HARD_ERROR:
523 : {
524 : enum MHD_Result ret;
525 :
526 0 : TALER_MERCHANTDB_rollback (TMH_db);
527 0 : GNUNET_break (0);
528 0 : ret = TALER_MHD_reply_with_error (
529 : connection,
530 : MHD_HTTP_INTERNAL_SERVER_ERROR,
531 : TALER_EC_GENERIC_DB_STORE_FAILED,
532 : "insert_instance");
533 0 : mi->rc = 1;
534 0 : TMH_instance_decref (mi);
535 0 : GNUNET_JSON_parse_free (spec);
536 0 : return ret;
537 : }
538 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
539 0 : goto retry;
540 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
541 : {
542 : enum MHD_Result ret;
543 :
544 0 : TALER_MERCHANTDB_rollback (TMH_db);
545 0 : GNUNET_break (0);
546 0 : ret = TALER_MHD_reply_with_error (
547 : connection,
548 : MHD_HTTP_CONFLICT,
549 : TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS,
550 : id_lower);
551 0 : mi->rc = 1;
552 0 : TMH_instance_decref (mi);
553 0 : GNUNET_JSON_parse_free (spec);
554 0 : return ret;
555 : }
556 37 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
557 : /* handled below */
558 37 : break;
559 : }
560 37 : qs = TALER_MERCHANTDB_commit (TMH_db);
561 37 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
562 37 : qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
563 0 : retry:
564 37 : if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
565 37 : break; /* success! -- or hard failure */
566 : } /* for .. MAX_RETRIES */
567 37 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
568 : {
569 0 : GNUNET_break (0);
570 0 : mi->rc = 1;
571 0 : TMH_instance_decref (mi);
572 0 : GNUNET_JSON_parse_free (spec);
573 0 : return TALER_MHD_reply_with_error (connection,
574 : MHD_HTTP_INTERNAL_SERVER_ERROR,
575 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
576 : NULL);
577 : }
578 : /* Finally, also update our running process */
579 37 : GNUNET_assert (GNUNET_OK ==
580 : TMH_add_instance (mi));
581 37 : TMH_reload_instances (mi->settings.id);
582 : }
583 37 : GNUNET_JSON_parse_free (spec);
584 37 : if (GNUNET_TIME_relative_is_zero (login_token_expiration))
585 : {
586 37 : return TALER_MHD_reply_static (connection,
587 : MHD_HTTP_NO_CONTENT,
588 : NULL,
589 : NULL,
590 : 0);
591 : }
592 :
593 : {
594 : /* Narrow DB interaction to new instance */
595 : enum GNUNET_DB_QueryStatus qs;
596 :
597 0 : qs = TALER_MERCHANTDB_set_instance (TMH_db,
598 : id_lower);
599 0 : switch (qs)
600 : {
601 0 : case GNUNET_DB_STATUS_HARD_ERROR:
602 0 : GNUNET_break (0);
603 0 : return TALER_MHD_reply_with_error (
604 : connection,
605 : MHD_HTTP_INTERNAL_SERVER_ERROR,
606 : TALER_EC_GENERIC_DB_SETUP_FAILED,
607 : "set_instance");
608 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
609 0 : GNUNET_break (0);
610 0 : return TALER_MHD_reply_with_error (
611 : connection,
612 : MHD_HTTP_INTERNAL_SERVER_ERROR,
613 : TALER_EC_GENERIC_DB_SETUP_FAILED,
614 : "set_instance");
615 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
616 0 : return TALER_MHD_reply_with_error (
617 : connection,
618 : MHD_HTTP_NOT_FOUND,
619 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
620 : hc->url);
621 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
622 0 : break;
623 : }
624 : }
625 :
626 : {
627 : struct TALER_MERCHANTDB_LoginTokenP btoken;
628 0 : enum TMH_AuthScope iscope = TMH_AS_REFRESHABLE | TMH_AS_SPA;
629 : enum GNUNET_DB_QueryStatus qs;
630 : struct GNUNET_TIME_Timestamp expiration_time;
631 0 : bool refreshable = true;
632 :
633 0 : GNUNET_CRYPTO_random_block (&btoken,
634 : sizeof (btoken));
635 : expiration_time
636 0 : = GNUNET_TIME_relative_to_timestamp (login_token_expiration);
637 0 : qs = TALER_MERCHANTDB_insert_login_token (TMH_db,
638 : id_lower,
639 : &btoken,
640 : GNUNET_TIME_timestamp_get (),
641 : expiration_time,
642 : iscope,
643 : "login token from instance creation");
644 0 : switch (qs)
645 : {
646 0 : case GNUNET_DB_STATUS_HARD_ERROR:
647 : case GNUNET_DB_STATUS_SOFT_ERROR:
648 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
649 0 : GNUNET_break (0);
650 0 : return TALER_MHD_reply_with_error (
651 : connection,
652 : MHD_HTTP_INTERNAL_SERVER_ERROR,
653 : TALER_EC_GENERIC_DB_STORE_FAILED,
654 : "insert_login_token");
655 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
656 0 : break;
657 : }
658 :
659 : {
660 : char *tok;
661 : enum MHD_Result ret;
662 : char *val;
663 :
664 0 : val = GNUNET_STRINGS_data_to_string_alloc (&btoken,
665 : sizeof (btoken));
666 0 : GNUNET_asprintf (&tok,
667 : RFC_8959_PREFIX "%s",
668 : val);
669 0 : GNUNET_free (val);
670 0 : ret = TALER_MHD_REPLY_JSON_PACK (
671 : connection,
672 : MHD_HTTP_OK,
673 : GNUNET_JSON_pack_string ("access_token",
674 : tok),
675 : GNUNET_JSON_pack_string ("token",
676 : tok),
677 : GNUNET_JSON_pack_string ("scope",
678 : TMH_get_name_by_scope (iscope,
679 : &refreshable)),
680 : GNUNET_JSON_pack_bool ("refreshable",
681 : refreshable),
682 : GNUNET_JSON_pack_timestamp ("expiration",
683 : expiration_time));
684 0 : GNUNET_free (tok);
685 0 : return ret;
686 : }
687 : }
688 : }
689 :
690 :
691 : /**
692 : * Generate an instance, given its configuration.
693 : *
694 : * @param rh context of the handler
695 : * @param connection the MHD connection to handle
696 : * @param[in,out] hc context with further information about the request
697 : * @return MHD result code
698 : */
699 : enum MHD_Result
700 37 : TMH_private_post_instances (const struct TMH_RequestHandler *rh,
701 : struct MHD_Connection *connection,
702 : struct TMH_HandlerContext *hc)
703 : {
704 74 : return post_instances (rh,
705 : connection,
706 : hc,
707 37 : GNUNET_TIME_UNIT_ZERO,
708 : false);
709 : }
710 :
711 :
712 : /**
713 : * Generate an instance, given its configuration.
714 : * Public handler to be used when self-provisioning.
715 : *
716 : * @param rh context of the handler
717 : * @param connection the MHD connection to handle
718 : * @param[in,out] hc context with further information about the request
719 : * @return MHD result code
720 : */
721 : enum MHD_Result
722 5 : TMH_public_post_instances (const struct TMH_RequestHandler *rh,
723 : struct MHD_Connection *connection,
724 : struct TMH_HandlerContext *hc)
725 : {
726 : struct GNUNET_TIME_Relative expiration;
727 :
728 5 : TALER_MHD_parse_request_rel_time (connection,
729 : "token_validity_ms",
730 : &expiration);
731 5 : if (GNUNET_YES !=
732 : TMH_have_self_provisioning)
733 : {
734 0 : GNUNET_break_op (0);
735 0 : return TALER_MHD_reply_with_error (connection,
736 : MHD_HTTP_FORBIDDEN,
737 : TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
738 : "Self-provisioning is not enabled");
739 : }
740 :
741 5 : return post_instances (rh,
742 : connection,
743 : hc,
744 : expiration,
745 : TMH_TCS_NONE !=
746 : TEH_mandatory_tan_channels);
747 : }
748 :
749 :
750 : /* end of taler-merchant-httpd_post-management-instances.c */
|