taler-merchant-httpd_post-management-instances.c (26263B)
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 GNUNET_JSON_spec_string ("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 'id' well-formed */ 230 { 231 static bool once; 232 static regex_t reg; 233 bool id_wellformed = true; 234 235 if (! once) 236 { 237 once = true; 238 GNUNET_assert (0 == 239 regcomp (®, 240 "^[A-Za-z0-9][A-Za-z0-9_.@-]+$", 241 REG_EXTENDED)); 242 } 243 244 if (0 != regexec (®, 245 is.id, 246 0, NULL, 0)) 247 id_wellformed = false; 248 if (! id_wellformed) 249 { 250 GNUNET_JSON_parse_free (spec); 251 GNUNET_free (is.phone); 252 return TALER_MHD_reply_with_error (connection, 253 MHD_HTTP_BAD_REQUEST, 254 TALER_EC_GENERIC_PARAMETER_MALFORMED, 255 "id"); 256 } 257 } 258 259 if (! TMH_location_object_valid (is.address)) 260 { 261 GNUNET_break_op (0); 262 GNUNET_JSON_parse_free (spec); 263 GNUNET_free (is.phone); 264 return TALER_MHD_reply_with_error (connection, 265 MHD_HTTP_BAD_REQUEST, 266 TALER_EC_GENERIC_PARAMETER_MALFORMED, 267 "address"); 268 } 269 270 if (! TMH_location_object_valid (is.jurisdiction)) 271 { 272 GNUNET_break_op (0); 273 GNUNET_JSON_parse_free (spec); 274 GNUNET_free (is.phone); 275 return TALER_MHD_reply_with_error (connection, 276 MHD_HTTP_BAD_REQUEST, 277 TALER_EC_GENERIC_PARAMETER_MALFORMED, 278 "jurisdiction"); 279 } 280 281 if ( (NULL != is.logo) && 282 (! TALER_MERCHANT_image_data_url_valid (is.logo)) ) 283 { 284 GNUNET_break_op (0); 285 GNUNET_JSON_parse_free (spec); 286 GNUNET_free (is.phone); 287 return TALER_MHD_reply_with_error (connection, 288 MHD_HTTP_BAD_REQUEST, 289 TALER_EC_GENERIC_PARAMETER_MALFORMED, 290 "logo"); 291 } 292 293 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 mi = TMH_lookup_instance (id_lower); 299 if (NULL != mi) 300 { 301 enum MHD_Result ret; 302 303 if (mi->deleted) 304 { 305 GNUNET_JSON_parse_free (spec); 306 GNUNET_free (is.phone); 307 GNUNET_free (id_lower); 308 return TALER_MHD_reply_with_error ( 309 connection, 310 MHD_HTTP_CONFLICT, 311 TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_PURGE_REQUIRED, 312 is.id); 313 } 314 /* Check for idempotency */ 315 if ( (0 == strcmp (mi->settings.id, 316 id_lower)) && 317 (0 == strcmp (mi->settings.name, 318 is.name)) && 319 ((mi->settings.email == is.email) || 320 (NULL != is.email && NULL != mi->settings.email && 321 0 == strcmp (mi->settings.email, 322 is.email))) && 323 ((mi->settings.website == is.website) || 324 (NULL != is.website && NULL != mi->settings.website && 325 0 == strcmp (mi->settings.website, 326 is.website))) && 327 ((mi->settings.logo == is.logo) || 328 (NULL != is.logo && NULL != mi->settings.logo && 329 0 == strcmp (mi->settings.logo, 330 is.logo))) && 331 ( ( (NULL != auth_password) && 332 (GNUNET_OK == 333 TMH_check_auth (auth_password, 334 &mi->auth.auth_salt, 335 &mi->auth.auth_hash)) ) || 336 ( (NULL == auth_password) && 337 (GNUNET_YES == 338 GNUNET_is_zero (&mi->auth.auth_hash))) ) && 339 (1 == json_equal (mi->settings.address, 340 is.address)) && 341 (1 == json_equal (mi->settings.jurisdiction, 342 is.jurisdiction)) && 343 (mi->settings.use_stefan == is.use_stefan) && 344 (GNUNET_TIME_relative_cmp (mi->settings.default_wire_transfer_delay, 345 ==, 346 is.default_wire_transfer_delay)) && 347 (GNUNET_TIME_relative_cmp (mi->settings.default_pay_delay, 348 ==, 349 is.default_pay_delay)) && 350 (GNUNET_TIME_relative_cmp (mi->settings.default_refund_delay, 351 ==, 352 is.default_refund_delay)) ) 353 { 354 GNUNET_JSON_parse_free (spec); 355 GNUNET_free (is.phone); 356 GNUNET_free (id_lower); 357 return TALER_MHD_reply_static (connection, 358 MHD_HTTP_NO_CONTENT, 359 NULL, 360 NULL, 361 0); 362 } 363 GNUNET_JSON_parse_free (spec); 364 GNUNET_free (is.phone); 365 ret = TALER_MHD_reply_with_error (connection, 366 MHD_HTTP_CONFLICT, 367 TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS, 368 id_lower); 369 GNUNET_free (id_lower); 370 return ret; 371 } 372 } 373 374 /* Check MFA is satisfied */ 375 if (validation_needed) 376 { 377 enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; 378 379 if ( (0 != (TMH_TCS_SMS & TEH_mandatory_tan_channels)) && 380 (NULL == is.phone) ) 381 { 382 GNUNET_break_op (0); 383 GNUNET_JSON_parse_free (spec); 384 GNUNET_free (is.phone); /* does nothing... */ 385 GNUNET_free (id_lower); 386 return TALER_MHD_reply_with_error (connection, 387 MHD_HTTP_BAD_REQUEST, 388 TALER_EC_GENERIC_PARAMETER_MISSING, 389 "phone_number"); 390 391 } 392 if ( (0 != (TMH_TCS_EMAIL & TEH_mandatory_tan_channels)) && 393 (NULL == is.email) ) 394 { 395 GNUNET_break_op (0); 396 GNUNET_JSON_parse_free (spec); 397 GNUNET_free (is.phone); 398 GNUNET_free (id_lower); 399 return TALER_MHD_reply_with_error (connection, 400 MHD_HTTP_BAD_REQUEST, 401 TALER_EC_GENERIC_PARAMETER_MISSING, 402 "email"); 403 } 404 switch (TEH_mandatory_tan_channels) 405 { 406 case TMH_TCS_NONE: 407 GNUNET_assert (0); 408 ret = GNUNET_OK; 409 break; 410 case TMH_TCS_SMS: 411 is.phone_validated = true; 412 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 break; 420 case TMH_TCS_EMAIL: 421 is.email_validated = true; 422 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 break; 430 case TMH_TCS_EMAIL_AND_SMS: 431 is.phone_validated = true; 432 is.email_validated = true; 433 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 break; 443 } 444 if (GNUNET_OK != ret) 445 { 446 GNUNET_JSON_parse_free (spec); 447 GNUNET_free (is.phone); 448 GNUNET_free (id_lower); 449 return (GNUNET_NO == ret) 450 ? MHD_YES 451 : MHD_NO; 452 } 453 } 454 455 /* handle authentication token setup */ 456 if (NULL == auth_password) 457 { 458 memset (&ias.auth_salt, 459 0, 460 sizeof (ias.auth_salt)); 461 memset (&ias.auth_hash, 462 0, 463 sizeof (ias.auth_hash)); 464 } 465 else 466 { 467 /* Sets 'auth_salt' and 'auth_hash' */ 468 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 mi = GNUNET_new (struct TMH_MerchantInstance); 479 mi->wm_head = wm_head; 480 mi->wm_tail = wm_tail; 481 mi->settings = is; 482 mi->settings.address = json_incref (mi->settings.address); 483 mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction); 484 mi->settings.id = id_lower; 485 mi->settings.name = GNUNET_strdup (is.name); 486 if (NULL != is.email) 487 mi->settings.email = GNUNET_strdup (is.email); 488 mi->settings.phone = is.phone; 489 is.phone = NULL; 490 if (NULL != is.website) 491 mi->settings.website = GNUNET_strdup (is.website); 492 if (NULL != is.logo) 493 mi->settings.logo = GNUNET_strdup (is.logo); 494 mi->auth = ias; 495 GNUNET_CRYPTO_eddsa_key_create (&mi->merchant_priv.eddsa_priv); 496 GNUNET_CRYPTO_eddsa_key_get_public (&mi->merchant_priv.eddsa_priv, 497 &mi->merchant_pub.eddsa_pub); 498 499 for (unsigned int i = 0; i<MAX_RETRIES; i++) 500 { 501 if (GNUNET_OK != 502 TALER_MERCHANTDB_start (TMH_db, 503 "post /instances")) 504 { 505 GNUNET_break (0); 506 mi->rc = 1; 507 TMH_instance_decref (mi); 508 GNUNET_JSON_parse_free (spec); 509 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 qs = TALER_MERCHANTDB_insert_instance (TMH_db, 516 &mi->merchant_pub, 517 &mi->merchant_priv, 518 &mi->settings, 519 &mi->auth); 520 switch (qs) 521 { 522 case GNUNET_DB_STATUS_HARD_ERROR: 523 { 524 enum MHD_Result ret; 525 526 TALER_MERCHANTDB_rollback (TMH_db); 527 GNUNET_break (0); 528 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 mi->rc = 1; 534 TMH_instance_decref (mi); 535 GNUNET_JSON_parse_free (spec); 536 return ret; 537 } 538 case GNUNET_DB_STATUS_SOFT_ERROR: 539 goto retry; 540 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 541 { 542 enum MHD_Result ret; 543 544 TALER_MERCHANTDB_rollback (TMH_db); 545 GNUNET_break (0); 546 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 mi->rc = 1; 552 TMH_instance_decref (mi); 553 GNUNET_JSON_parse_free (spec); 554 return ret; 555 } 556 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 557 /* handled below */ 558 break; 559 } 560 qs = TALER_MERCHANTDB_commit (TMH_db); 561 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 562 qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 563 retry: 564 if (GNUNET_DB_STATUS_SOFT_ERROR != qs) 565 break; /* success! -- or hard failure */ 566 } /* for .. MAX_RETRIES */ 567 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 568 { 569 GNUNET_break (0); 570 mi->rc = 1; 571 TMH_instance_decref (mi); 572 GNUNET_JSON_parse_free (spec); 573 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 GNUNET_assert (GNUNET_OK == 580 TMH_add_instance (mi)); 581 TMH_reload_instances (mi->settings.id); 582 } 583 GNUNET_JSON_parse_free (spec); 584 if (GNUNET_TIME_relative_is_zero (login_token_expiration)) 585 { 586 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 qs = TALER_MERCHANTDB_set_instance (TMH_db, 598 id_lower); 599 switch (qs) 600 { 601 case GNUNET_DB_STATUS_HARD_ERROR: 602 GNUNET_break (0); 603 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 case GNUNET_DB_STATUS_SOFT_ERROR: 609 GNUNET_break (0); 610 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 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 616 return TALER_MHD_reply_with_error ( 617 connection, 618 MHD_HTTP_NOT_FOUND, 619 TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN, 620 hc->url); 621 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 622 break; 623 } 624 } 625 626 { 627 struct TALER_MERCHANTDB_LoginTokenP btoken; 628 enum TMH_AuthScope iscope = TMH_AS_REFRESHABLE | TMH_AS_SPA; 629 enum GNUNET_DB_QueryStatus qs; 630 struct GNUNET_TIME_Timestamp expiration_time; 631 bool refreshable = true; 632 633 GNUNET_CRYPTO_random_block (&btoken, 634 sizeof (btoken)); 635 expiration_time 636 = GNUNET_TIME_relative_to_timestamp (login_token_expiration); 637 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 switch (qs) 645 { 646 case GNUNET_DB_STATUS_HARD_ERROR: 647 case GNUNET_DB_STATUS_SOFT_ERROR: 648 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 649 GNUNET_break (0); 650 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 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 656 break; 657 } 658 659 { 660 char *tok; 661 enum MHD_Result ret; 662 char *val; 663 664 val = GNUNET_STRINGS_data_to_string_alloc (&btoken, 665 sizeof (btoken)); 666 GNUNET_asprintf (&tok, 667 RFC_8959_PREFIX "%s", 668 val); 669 GNUNET_free (val); 670 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 GNUNET_free (tok); 685 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 TMH_private_post_instances (const struct TMH_RequestHandler *rh, 701 struct MHD_Connection *connection, 702 struct TMH_HandlerContext *hc) 703 { 704 return post_instances (rh, 705 connection, 706 hc, 707 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 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 TALER_MHD_parse_request_rel_time (connection, 729 "token_validity_ms", 730 &expiration); 731 if (GNUNET_YES != 732 TMH_have_self_provisioning) 733 { 734 GNUNET_break_op (0); 735 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 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 */