taler-merchant-httpd_post-management-instances.c (26217B)
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 is.id, 404 TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION, 405 true, 406 TALER_MERCHANT_MFA_CHANNEL_SMS, 407 is.phone, 408 TALER_MERCHANT_MFA_CHANNEL_NONE); 409 break; 410 case TMH_TCS_EMAIL: 411 is.email_validated = true; 412 ret = TMH_mfa_challenges_do (hc, 413 is.id, 414 TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION, 415 true, 416 TALER_MERCHANT_MFA_CHANNEL_EMAIL, 417 is.email, 418 TALER_MERCHANT_MFA_CHANNEL_NONE); 419 break; 420 case TMH_TCS_EMAIL_AND_SMS: 421 is.phone_validated = true; 422 is.email_validated = true; 423 ret = TMH_mfa_challenges_do (hc, 424 is.id, 425 TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION, 426 true, 427 TALER_MERCHANT_MFA_CHANNEL_EMAIL, 428 is.email, 429 TALER_MERCHANT_MFA_CHANNEL_SMS, 430 is.phone, 431 TALER_MERCHANT_MFA_CHANNEL_NONE); 432 break; 433 } 434 if (GNUNET_OK != ret) 435 { 436 GNUNET_JSON_parse_free (spec); 437 GNUNET_free (is.phone); 438 return (GNUNET_NO == ret) 439 ? MHD_YES 440 : MHD_NO; 441 } 442 } 443 444 /* handle authentication token setup */ 445 if (NULL == auth_password) 446 { 447 memset (&ias.auth_salt, 448 0, 449 sizeof (ias.auth_salt)); 450 memset (&ias.auth_hash, 451 0, 452 sizeof (ias.auth_hash)); 453 } 454 else 455 { 456 /* Sets 'auth_salt' and 'auth_hash' */ 457 TMH_compute_auth (auth_password, 458 &ias.auth_salt, 459 &ias.auth_hash); 460 } 461 462 /* create in-memory data structure */ 463 { 464 struct TMH_MerchantInstance *mi; 465 enum GNUNET_DB_QueryStatus qs; 466 467 mi = GNUNET_new (struct TMH_MerchantInstance); 468 mi->wm_head = wm_head; 469 mi->wm_tail = wm_tail; 470 mi->settings = is; 471 mi->settings.address = json_incref (mi->settings.address); 472 mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction); 473 mi->settings.id = GNUNET_STRINGS_utf8_tolower (is.id); 474 mi->settings.name = GNUNET_strdup (is.name); 475 if (NULL != is.email) 476 mi->settings.email = GNUNET_strdup (is.email); 477 mi->settings.phone = is.phone; 478 is.phone = NULL; 479 if (NULL != is.website) 480 mi->settings.website = GNUNET_strdup (is.website); 481 if (NULL != is.logo) 482 mi->settings.logo = GNUNET_strdup (is.logo); 483 mi->auth = ias; 484 GNUNET_CRYPTO_eddsa_key_create (&mi->merchant_priv.eddsa_priv); 485 GNUNET_CRYPTO_eddsa_key_get_public (&mi->merchant_priv.eddsa_priv, 486 &mi->merchant_pub.eddsa_pub); 487 488 for (unsigned int i = 0; i<MAX_RETRIES; i++) 489 { 490 if (GNUNET_OK != 491 TALER_MERCHANTDB_start (TMH_db, 492 "post /instances")) 493 { 494 GNUNET_break (0); 495 mi->rc = 1; 496 TMH_instance_decref (mi); 497 GNUNET_JSON_parse_free (spec); 498 return TALER_MHD_reply_with_error (connection, 499 MHD_HTTP_INTERNAL_SERVER_ERROR, 500 TALER_EC_GENERIC_DB_START_FAILED, 501 NULL); 502 } 503 qs = TALER_MERCHANTDB_insert_instance (TMH_db, 504 &mi->merchant_pub, 505 &mi->merchant_priv, 506 &mi->settings, 507 &mi->auth); 508 switch (qs) 509 { 510 case GNUNET_DB_STATUS_HARD_ERROR: 511 { 512 enum MHD_Result ret; 513 514 TALER_MERCHANTDB_rollback (TMH_db); 515 GNUNET_break (0); 516 ret = TALER_MHD_reply_with_error (connection, 517 MHD_HTTP_INTERNAL_SERVER_ERROR, 518 TALER_EC_GENERIC_DB_STORE_FAILED, 519 is.id); 520 mi->rc = 1; 521 TMH_instance_decref (mi); 522 GNUNET_JSON_parse_free (spec); 523 return ret; 524 } 525 case GNUNET_DB_STATUS_SOFT_ERROR: 526 goto retry; 527 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 528 { 529 enum MHD_Result ret; 530 531 TALER_MERCHANTDB_rollback (TMH_db); 532 GNUNET_break (0); 533 ret = TALER_MHD_reply_with_error (connection, 534 MHD_HTTP_CONFLICT, 535 TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS, 536 is.id); 537 mi->rc = 1; 538 TMH_instance_decref (mi); 539 GNUNET_JSON_parse_free (spec); 540 return ret; 541 } 542 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 543 /* handled below */ 544 break; 545 } 546 qs = TALER_MERCHANTDB_commit (TMH_db); 547 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 548 qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 549 retry: 550 if (GNUNET_DB_STATUS_SOFT_ERROR != qs) 551 break; /* success! -- or hard failure */ 552 } /* for .. MAX_RETRIES */ 553 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 554 { 555 GNUNET_break (0); 556 mi->rc = 1; 557 TMH_instance_decref (mi); 558 GNUNET_JSON_parse_free (spec); 559 return TALER_MHD_reply_with_error (connection, 560 MHD_HTTP_INTERNAL_SERVER_ERROR, 561 TALER_EC_GENERIC_DB_COMMIT_FAILED, 562 NULL); 563 } 564 /* Finally, also update our running process */ 565 GNUNET_assert (GNUNET_OK == 566 TMH_add_instance (mi)); 567 TMH_reload_instances (mi->settings.id); 568 } 569 GNUNET_JSON_parse_free (spec); 570 if (GNUNET_TIME_relative_is_zero (login_token_expiration)) 571 { 572 return TALER_MHD_reply_static (connection, 573 MHD_HTTP_NO_CONTENT, 574 NULL, 575 NULL, 576 0); 577 } 578 579 { 580 /* Narrow DB interaction to new instance */ 581 enum GNUNET_DB_QueryStatus qs; 582 583 qs = TALER_MERCHANTDB_set_instance (TMH_db, 584 is.id); 585 switch (qs) 586 { 587 case GNUNET_DB_STATUS_HARD_ERROR: 588 GNUNET_break (0); 589 return TALER_MHD_reply_with_error ( 590 connection, 591 MHD_HTTP_INTERNAL_SERVER_ERROR, 592 TALER_EC_GENERIC_DB_SETUP_FAILED, 593 "set_instance"); 594 case GNUNET_DB_STATUS_SOFT_ERROR: 595 GNUNET_break (0); 596 return TALER_MHD_reply_with_error ( 597 connection, 598 MHD_HTTP_INTERNAL_SERVER_ERROR, 599 TALER_EC_GENERIC_DB_SETUP_FAILED, 600 "set_instance"); 601 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 602 return TALER_MHD_reply_with_error ( 603 connection, 604 MHD_HTTP_NOT_FOUND, 605 TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN, 606 hc->url); 607 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 608 break; 609 } 610 } 611 612 { 613 struct TALER_MERCHANTDB_LoginTokenP btoken; 614 enum TMH_AuthScope iscope = TMH_AS_REFRESHABLE | TMH_AS_SPA; 615 enum GNUNET_DB_QueryStatus qs; 616 struct GNUNET_TIME_Timestamp expiration_time; 617 bool refreshable = true; 618 619 GNUNET_CRYPTO_random_block (&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 */