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