taler-merchant-httpd_post-management-instances.c (26261B)
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 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 struct TALER_MERCHANTDB_InstanceSettings is = { 0 }; 67 struct TALER_MERCHANTDB_InstanceAuthSettings ias; 68 const char *auth_password = NULL; 69 struct TMH_WireMethod *wm_head = NULL; 70 struct TMH_WireMethod *wm_tail = NULL; 71 const json_t *jauth; 72 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 TALER_JSON_spec_slug ("id", 80 (const char **) &is.id), 81 GNUNET_JSON_spec_string ("name", 82 (const char **) &is.name), 83 GNUNET_JSON_spec_mark_optional ( 84 GNUNET_JSON_spec_string ("email", 85 (const char **) &is.email), 86 NULL), 87 GNUNET_JSON_spec_mark_optional ( 88 GNUNET_JSON_spec_string ("phone_number", 89 &iphone), 90 NULL), 91 GNUNET_JSON_spec_mark_optional ( 92 TALER_JSON_spec_web_url ("website", 93 (const char **) &is.website), 94 NULL), 95 GNUNET_JSON_spec_mark_optional ( 96 GNUNET_JSON_spec_string ("logo", 97 (const char **) &is.logo), 98 NULL), 99 GNUNET_JSON_spec_object_const ("auth", 100 &jauth), 101 GNUNET_JSON_spec_json ("address", 102 &is.address), 103 GNUNET_JSON_spec_json ("jurisdiction", 104 &is.jurisdiction), 105 GNUNET_JSON_spec_bool ("use_stefan", 106 &is.use_stefan), 107 GNUNET_JSON_spec_mark_optional ( 108 GNUNET_JSON_spec_relative_time ("default_pay_delay", 109 &is.default_pay_delay), 110 &no_pay_delay), 111 GNUNET_JSON_spec_mark_optional ( 112 GNUNET_JSON_spec_relative_time ("default_refund_delay", 113 &is.default_refund_delay), 114 &no_refund_delay), 115 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 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 GNUNET_JSON_spec_end () 125 }; 126 127 { 128 enum GNUNET_GenericReturnValue res; 129 130 res = TALER_MHD_parse_json_data (connection, 131 hc->request_body, 132 spec); 133 if (GNUNET_OK != res) 134 return (GNUNET_NO == res) 135 ? MHD_YES 136 : MHD_NO; 137 } 138 if (no_pay_delay) 139 is.default_pay_delay = TMH_default_pay_delay; 140 if (no_refund_delay) 141 is.default_refund_delay = TMH_default_refund_delay; 142 if (no_transfer_delay) 143 is.default_wire_transfer_delay = TMH_default_wire_transfer_delay; 144 if (no_rounding_interval) 145 is.default_wire_transfer_rounding_interval 146 = TMH_default_wire_transfer_rounding_interval; 147 if (GNUNET_TIME_relative_is_forever (is.default_pay_delay)) 148 { 149 GNUNET_break_op (0); 150 GNUNET_JSON_parse_free (spec); 151 return TALER_MHD_reply_with_error (connection, 152 MHD_HTTP_BAD_REQUEST, 153 TALER_EC_GENERIC_PARAMETER_MALFORMED, 154 "default_pay_delay"); 155 } 156 if (GNUNET_TIME_relative_is_forever (is.default_refund_delay)) 157 { 158 GNUNET_break_op (0); 159 GNUNET_JSON_parse_free (spec); 160 return TALER_MHD_reply_with_error (connection, 161 MHD_HTTP_BAD_REQUEST, 162 TALER_EC_GENERIC_PARAMETER_MALFORMED, 163 "default_refund_delay"); 164 } 165 if (GNUNET_TIME_relative_is_forever (is.default_wire_transfer_delay)) 166 { 167 GNUNET_break_op (0); 168 GNUNET_JSON_parse_free (spec); 169 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 if (NULL != iphone) 175 { 176 is.phone = TALER_MERCHANT_phone_validate_normalize (iphone, 177 false); 178 if (NULL == is.phone) 179 { 180 GNUNET_break_op (0); 181 GNUNET_JSON_parse_free (spec); 182 return TALER_MHD_reply_with_error (connection, 183 MHD_HTTP_BAD_REQUEST, 184 TALER_EC_GENERIC_PARAMETER_MALFORMED, 185 "phone_number"); 186 } 187 if ( (NULL != TMH_phone_regex) && 188 (0 != 189 regexec (&TMH_phone_rx, 190 is.phone, 191 0, 192 NULL, 193 0)) ) 194 { 195 GNUNET_break_op (0); 196 GNUNET_JSON_parse_free (spec); 197 return TALER_MHD_reply_with_error (connection, 198 MHD_HTTP_BAD_REQUEST, 199 TALER_EC_GENERIC_PARAMETER_MALFORMED, 200 "phone_number"); 201 } 202 } 203 if ( (NULL != is.email) && 204 (! TALER_MERCHANT_email_valid (is.email)) ) 205 { 206 GNUNET_break_op (0); 207 GNUNET_JSON_parse_free (spec); 208 GNUNET_free (is.phone); 209 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 ret = TMH_check_auth_config (connection, 219 jauth, 220 &auth_password); 221 if (GNUNET_OK != ret) 222 { 223 GNUNET_free (is.phone); 224 GNUNET_JSON_parse_free (spec); 225 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; 226 } 227 } 228 229 /* check extra constraints on instance IDs beyond is 'slug' */ 230 { 231 bool id_wellformed = true; 232 233 if ('\0' == is.id[1]) 234 id_wellformed = false; /* at least two characters */ 235 if (! ( ( ('a' <= is.id[0]) && ('z' >= is.id[0]) ) || 236 ( ('A' <= is.id[0]) && ('Z' >= is.id[0]) ) || 237 ( ('0' <= is.id[0]) && ('9' >= is.id[0]) ) ) ) 238 id_wellformed = false; /* must start alphanumeric */ 239 if (! id_wellformed) 240 { 241 GNUNET_break_op (0); 242 GNUNET_JSON_parse_free (spec); 243 GNUNET_free (is.phone); 244 return TALER_MHD_reply_with_error (connection, 245 MHD_HTTP_BAD_REQUEST, 246 TALER_EC_GENERIC_PARAMETER_MALFORMED, 247 "id"); 248 } 249 } 250 251 if (! TMH_location_object_valid (is.address)) 252 { 253 GNUNET_break_op (0); 254 GNUNET_JSON_parse_free (spec); 255 GNUNET_free (is.phone); 256 return TALER_MHD_reply_with_error (connection, 257 MHD_HTTP_BAD_REQUEST, 258 TALER_EC_GENERIC_PARAMETER_MALFORMED, 259 "address"); 260 } 261 262 if (! TMH_location_object_valid (is.jurisdiction)) 263 { 264 GNUNET_break_op (0); 265 GNUNET_JSON_parse_free (spec); 266 GNUNET_free (is.phone); 267 return TALER_MHD_reply_with_error (connection, 268 MHD_HTTP_BAD_REQUEST, 269 TALER_EC_GENERIC_PARAMETER_MALFORMED, 270 "jurisdiction"); 271 } 272 273 if ( (NULL != is.logo) && 274 (! TALER_MERCHANT_image_data_url_valid (is.logo)) ) 275 { 276 GNUNET_break_op (0); 277 GNUNET_JSON_parse_free (spec); 278 GNUNET_free (is.phone); 279 return TALER_MHD_reply_with_error (connection, 280 MHD_HTTP_BAD_REQUEST, 281 TALER_EC_GENERIC_PARAMETER_MALFORMED, 282 "logo"); 283 } 284 285 id_lower = GNUNET_STRINGS_utf8_tolower (is.id); 286 { 287 /* Test if an instance of this id is known */ 288 struct TMH_MerchantInstance *mi; 289 290 mi = TMH_lookup_instance (id_lower); 291 if (NULL != mi) 292 { 293 enum MHD_Result ret; 294 295 if (mi->deleted) 296 { 297 GNUNET_JSON_parse_free (spec); 298 GNUNET_free (is.phone); 299 GNUNET_free (id_lower); 300 return TALER_MHD_reply_with_error ( 301 connection, 302 MHD_HTTP_CONFLICT, 303 TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_PURGE_REQUIRED, 304 is.id); 305 } 306 /* Check for idempotency */ 307 if ( (0 == strcmp (mi->settings.id, 308 id_lower)) && 309 (0 == strcmp (mi->settings.name, 310 is.name)) && 311 ((mi->settings.email == is.email) || 312 (NULL != is.email && NULL != mi->settings.email && 313 0 == strcmp (mi->settings.email, 314 is.email))) && 315 ((mi->settings.website == is.website) || 316 (NULL != is.website && NULL != mi->settings.website && 317 0 == strcmp (mi->settings.website, 318 is.website))) && 319 ((mi->settings.logo == is.logo) || 320 (NULL != is.logo && NULL != mi->settings.logo && 321 0 == strcmp (mi->settings.logo, 322 is.logo))) && 323 ( ( (NULL != auth_password) && 324 (GNUNET_OK == 325 TMH_check_auth (auth_password, 326 &mi->auth.auth_salt, 327 &mi->auth.auth_hash)) ) || 328 ( (NULL == auth_password) && 329 (GNUNET_YES == 330 GNUNET_is_zero (&mi->auth.auth_hash))) ) && 331 (1 == json_equal (mi->settings.address, 332 is.address)) && 333 (1 == json_equal (mi->settings.jurisdiction, 334 is.jurisdiction)) && 335 (mi->settings.use_stefan == is.use_stefan) && 336 (GNUNET_TIME_relative_cmp (mi->settings.default_wire_transfer_delay, 337 ==, 338 is.default_wire_transfer_delay)) && 339 (GNUNET_TIME_relative_cmp (mi->settings.default_pay_delay, 340 ==, 341 is.default_pay_delay)) && 342 (GNUNET_TIME_relative_cmp (mi->settings.default_refund_delay, 343 ==, 344 is.default_refund_delay)) ) 345 { 346 GNUNET_JSON_parse_free (spec); 347 GNUNET_free (is.phone); 348 GNUNET_free (id_lower); 349 return TALER_MHD_reply_static (connection, 350 MHD_HTTP_NO_CONTENT, 351 NULL, 352 NULL, 353 0); 354 } 355 GNUNET_JSON_parse_free (spec); 356 GNUNET_free (is.phone); 357 ret = TALER_MHD_reply_with_error (connection, 358 MHD_HTTP_CONFLICT, 359 TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS, 360 id_lower); 361 GNUNET_free (id_lower); 362 return ret; 363 } 364 } 365 366 /* Check MFA is satisfied */ 367 if (validation_needed) 368 { 369 enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; 370 371 if ( (0 != (TMH_TCS_SMS & TEH_mandatory_tan_channels)) && 372 (NULL == is.phone) ) 373 { 374 GNUNET_break_op (0); 375 GNUNET_JSON_parse_free (spec); 376 GNUNET_free (is.phone); /* does nothing... */ 377 GNUNET_free (id_lower); 378 return TALER_MHD_reply_with_error (connection, 379 MHD_HTTP_BAD_REQUEST, 380 TALER_EC_GENERIC_PARAMETER_MISSING, 381 "phone_number"); 382 383 } 384 if ( (0 != (TMH_TCS_EMAIL & TEH_mandatory_tan_channels)) && 385 (NULL == is.email) ) 386 { 387 GNUNET_break_op (0); 388 GNUNET_JSON_parse_free (spec); 389 GNUNET_free (is.phone); 390 GNUNET_free (id_lower); 391 return TALER_MHD_reply_with_error (connection, 392 MHD_HTTP_BAD_REQUEST, 393 TALER_EC_GENERIC_PARAMETER_MISSING, 394 "email"); 395 } 396 switch (TEH_mandatory_tan_channels) 397 { 398 case TMH_TCS_NONE: 399 GNUNET_assert (0); 400 ret = GNUNET_OK; 401 break; 402 case TMH_TCS_SMS: 403 is.phone_validated = true; 404 ret = TMH_mfa_challenges_do (hc, 405 id_lower, 406 TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION, 407 true, 408 TALER_MERCHANT_MFA_CHANNEL_SMS, 409 is.phone, 410 TALER_MERCHANT_MFA_CHANNEL_NONE); 411 break; 412 case TMH_TCS_EMAIL: 413 is.email_validated = true; 414 ret = TMH_mfa_challenges_do (hc, 415 id_lower, 416 TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION, 417 true, 418 TALER_MERCHANT_MFA_CHANNEL_EMAIL, 419 is.email, 420 TALER_MERCHANT_MFA_CHANNEL_NONE); 421 break; 422 case TMH_TCS_EMAIL_AND_SMS: 423 is.phone_validated = true; 424 is.email_validated = true; 425 ret = TMH_mfa_challenges_do (hc, 426 id_lower, 427 TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION, 428 true, 429 TALER_MERCHANT_MFA_CHANNEL_EMAIL, 430 is.email, 431 TALER_MERCHANT_MFA_CHANNEL_SMS, 432 is.phone, 433 TALER_MERCHANT_MFA_CHANNEL_NONE); 434 break; 435 } 436 if (GNUNET_OK != ret) 437 { 438 GNUNET_JSON_parse_free (spec); 439 GNUNET_free (is.phone); 440 GNUNET_free (id_lower); 441 return (GNUNET_NO == ret) 442 ? MHD_YES 443 : MHD_NO; 444 } 445 } 446 447 /* handle authentication token setup */ 448 if (NULL == auth_password) 449 { 450 memset (&ias.auth_salt, 451 0, 452 sizeof (ias.auth_salt)); 453 memset (&ias.auth_hash, 454 0, 455 sizeof (ias.auth_hash)); 456 } 457 else 458 { 459 /* Sets 'auth_salt' and 'auth_hash' */ 460 TMH_compute_auth (auth_password, 461 &ias.auth_salt, 462 &ias.auth_hash); 463 } 464 465 /* create in-memory data structure */ 466 { 467 struct TMH_MerchantInstance *mi; 468 enum GNUNET_DB_QueryStatus qs; 469 470 mi = GNUNET_new (struct TMH_MerchantInstance); 471 mi->wm_head = wm_head; 472 mi->wm_tail = wm_tail; 473 mi->settings = is; 474 mi->settings.address = json_incref (mi->settings.address); 475 mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction); 476 mi->settings.id = id_lower; 477 mi->settings.name = GNUNET_strdup (is.name); 478 if (NULL != is.email) 479 mi->settings.email = GNUNET_strdup (is.email); 480 mi->settings.phone = is.phone; 481 is.phone = NULL; 482 if (NULL != is.website) 483 mi->settings.website = GNUNET_strdup (is.website); 484 if (NULL != is.logo) 485 mi->settings.logo = GNUNET_strdup (is.logo); 486 mi->auth = ias; 487 GNUNET_CRYPTO_eddsa_key_create (&mi->merchant_priv.eddsa_priv); 488 GNUNET_CRYPTO_eddsa_key_get_public (&mi->merchant_priv.eddsa_priv, 489 &mi->merchant_pub.eddsa_pub); 490 491 for (unsigned int i = 0; i<MAX_RETRIES; i++) 492 { 493 if (GNUNET_OK != 494 TALER_MERCHANTDB_start (TMH_db, 495 "post /instances")) 496 { 497 GNUNET_break (0); 498 mi->rc = 1; 499 TMH_instance_decref (mi); 500 GNUNET_JSON_parse_free (spec); 501 return TALER_MHD_reply_with_error ( 502 connection, 503 MHD_HTTP_INTERNAL_SERVER_ERROR, 504 TALER_EC_GENERIC_DB_START_FAILED, 505 NULL); 506 } 507 qs = TALER_MERCHANTDB_insert_instance (TMH_db, 508 &mi->merchant_pub, 509 &mi->merchant_priv, 510 &mi->settings, 511 &mi->auth); 512 switch (qs) 513 { 514 case GNUNET_DB_STATUS_HARD_ERROR: 515 { 516 enum MHD_Result ret; 517 518 TALER_MERCHANTDB_rollback (TMH_db); 519 GNUNET_break (0); 520 ret = TALER_MHD_reply_with_error ( 521 connection, 522 MHD_HTTP_INTERNAL_SERVER_ERROR, 523 TALER_EC_GENERIC_DB_STORE_FAILED, 524 "insert_instance"); 525 mi->rc = 1; 526 TMH_instance_decref (mi); 527 GNUNET_JSON_parse_free (spec); 528 return ret; 529 } 530 case GNUNET_DB_STATUS_SOFT_ERROR: 531 goto retry; 532 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 533 { 534 enum MHD_Result ret; 535 536 TALER_MERCHANTDB_rollback (TMH_db); 537 GNUNET_break (0); 538 ret = TALER_MHD_reply_with_error ( 539 connection, 540 MHD_HTTP_CONFLICT, 541 TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS, 542 id_lower); 543 mi->rc = 1; 544 TMH_instance_decref (mi); 545 GNUNET_JSON_parse_free (spec); 546 return ret; 547 } 548 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 549 /* handled below */ 550 break; 551 } 552 qs = TALER_MERCHANTDB_commit (TMH_db); 553 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 554 qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 555 retry: 556 if (GNUNET_DB_STATUS_SOFT_ERROR != qs) 557 break; /* success! -- or hard failure */ 558 } /* for .. MAX_RETRIES */ 559 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 560 { 561 GNUNET_break (0); 562 mi->rc = 1; 563 TMH_instance_decref (mi); 564 GNUNET_JSON_parse_free (spec); 565 return TALER_MHD_reply_with_error (connection, 566 MHD_HTTP_INTERNAL_SERVER_ERROR, 567 TALER_EC_GENERIC_DB_COMMIT_FAILED, 568 NULL); 569 } 570 /* Finally, also update our running process */ 571 GNUNET_assert (GNUNET_OK == 572 TMH_add_instance (mi)); 573 TMH_reload_instances (mi->settings.id); 574 } 575 GNUNET_JSON_parse_free (spec); 576 if (GNUNET_TIME_relative_is_zero (login_token_expiration)) 577 { 578 return TALER_MHD_reply_static (connection, 579 MHD_HTTP_NO_CONTENT, 580 NULL, 581 NULL, 582 0); 583 } 584 585 { 586 /* Narrow DB interaction to new instance */ 587 enum GNUNET_DB_QueryStatus qs; 588 589 qs = TALER_MERCHANTDB_set_instance (TMH_db, 590 id_lower); 591 switch (qs) 592 { 593 case GNUNET_DB_STATUS_HARD_ERROR: 594 GNUNET_break (0); 595 return TALER_MHD_reply_with_error ( 596 connection, 597 MHD_HTTP_INTERNAL_SERVER_ERROR, 598 TALER_EC_GENERIC_DB_SETUP_FAILED, 599 "set_instance"); 600 case GNUNET_DB_STATUS_SOFT_ERROR: 601 GNUNET_break (0); 602 return TALER_MHD_reply_with_error ( 603 connection, 604 MHD_HTTP_INTERNAL_SERVER_ERROR, 605 TALER_EC_GENERIC_DB_SETUP_FAILED, 606 "set_instance"); 607 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 608 return TALER_MHD_reply_with_error ( 609 connection, 610 MHD_HTTP_NOT_FOUND, 611 TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN, 612 hc->url); 613 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 614 break; 615 } 616 } 617 618 { 619 struct TALER_MERCHANTDB_LoginTokenP btoken; 620 enum TMH_AuthScope iscope = TMH_AS_REFRESHABLE | TMH_AS_SPA; 621 enum GNUNET_DB_QueryStatus qs; 622 struct GNUNET_TIME_Timestamp expiration_time; 623 bool refreshable = true; 624 625 GNUNET_CRYPTO_random_block (&btoken, 626 sizeof (btoken)); 627 expiration_time 628 = GNUNET_TIME_relative_to_timestamp (login_token_expiration); 629 qs = TALER_MERCHANTDB_insert_login_token (TMH_db, 630 id_lower, 631 &btoken, 632 GNUNET_TIME_timestamp_get (), 633 expiration_time, 634 iscope, 635 "login token from instance creation"); 636 switch (qs) 637 { 638 case GNUNET_DB_STATUS_HARD_ERROR: 639 case GNUNET_DB_STATUS_SOFT_ERROR: 640 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 641 GNUNET_break (0); 642 return TALER_MHD_reply_with_error ( 643 connection, 644 MHD_HTTP_INTERNAL_SERVER_ERROR, 645 TALER_EC_GENERIC_DB_STORE_FAILED, 646 "insert_login_token"); 647 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 648 break; 649 } 650 651 { 652 char *tok; 653 enum MHD_Result ret; 654 char *val; 655 656 val = GNUNET_STRINGS_data_to_string_alloc (&btoken, 657 sizeof (btoken)); 658 GNUNET_asprintf (&tok, 659 RFC_8959_PREFIX "%s", 660 val); 661 GNUNET_free (val); 662 ret = TALER_MHD_REPLY_JSON_PACK ( 663 connection, 664 MHD_HTTP_OK, 665 GNUNET_JSON_pack_string ("access_token", 666 tok), 667 GNUNET_JSON_pack_string ("token", 668 tok), 669 GNUNET_JSON_pack_string ("scope", 670 TMH_get_name_by_scope (iscope, 671 &refreshable)), 672 GNUNET_JSON_pack_bool ("refreshable", 673 refreshable), 674 GNUNET_JSON_pack_timestamp ("expiration", 675 expiration_time)); 676 GNUNET_free (tok); 677 return ret; 678 } 679 } 680 } 681 682 683 /** 684 * Generate an instance, given its configuration. 685 * 686 * @param rh context of the handler 687 * @param connection the MHD connection to handle 688 * @param[in,out] hc context with further information about the request 689 * @return MHD result code 690 */ 691 enum MHD_Result 692 TMH_private_post_instances (const struct TMH_RequestHandler *rh, 693 struct MHD_Connection *connection, 694 struct TMH_HandlerContext *hc) 695 { 696 return post_instances (rh, 697 connection, 698 hc, 699 GNUNET_TIME_UNIT_ZERO, 700 false); 701 } 702 703 704 /** 705 * Generate an instance, given its configuration. 706 * Public handler to be used when self-provisioning. 707 * 708 * @param rh context of the handler 709 * @param connection the MHD connection to handle 710 * @param[in,out] hc context with further information about the request 711 * @return MHD result code 712 */ 713 enum MHD_Result 714 TMH_public_post_instances (const struct TMH_RequestHandler *rh, 715 struct MHD_Connection *connection, 716 struct TMH_HandlerContext *hc) 717 { 718 struct GNUNET_TIME_Relative expiration; 719 720 TALER_MHD_parse_request_rel_time (connection, 721 "token_validity_ms", 722 &expiration); 723 if (GNUNET_YES != 724 TMH_have_self_provisioning) 725 { 726 GNUNET_break_op (0); 727 return TALER_MHD_reply_with_error (connection, 728 MHD_HTTP_FORBIDDEN, 729 TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED, 730 "Self-provisioning is not enabled"); 731 } 732 733 return post_instances (rh, 734 connection, 735 hc, 736 expiration, 737 TMH_TCS_NONE != 738 TEH_mandatory_tan_channels); 739 } 740 741 742 /* end of taler-merchant-httpd_post-management-instances.c */