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 taler-merchant-httpd_private-post-instances.c
22 : * @brief implementing POST /instances request handling
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include "taler-merchant-httpd_private-post-instances.h"
27 : #include "taler-merchant-httpd_helper.h"
28 : #include "taler-merchant-httpd.h"
29 : #include "taler-merchant-httpd_mfa.h"
30 : #include "taler_merchant_bank_lib.h"
31 : #include <taler/taler_dbevents.h>
32 : #include <taler/taler_json_lib.h>
33 : #include <regex.h>
34 :
35 : /**
36 : * How often do we retry the simple INSERT database transaction?
37 : */
38 : #define MAX_RETRIES 3
39 :
40 :
41 : /**
42 : * Generate an instance, given its configuration.
43 : *
44 : * @param rh context of the handler
45 : * @param connection the MHD connection to handle
46 : * @param[in,out] hc context with further information about the request
47 : * @param login_token_expiration set to how long a login token validity
48 : * should be, use zero if no login token should be created
49 : * @param validation_needed true if self-provisioned and
50 : * email/phone registration is required before the
51 : * instance can become fully active
52 : * @return MHD result code
53 : */
54 : static MHD_RESULT
55 33 : post_instances (const struct TMH_RequestHandler *rh,
56 : struct MHD_Connection *connection,
57 : struct TMH_HandlerContext *hc,
58 : struct GNUNET_TIME_Relative login_token_expiration,
59 : bool validation_needed)
60 : {
61 33 : struct TALER_MERCHANTDB_InstanceSettings is = { 0 };
62 : struct TALER_MERCHANTDB_InstanceAuthSettings ias;
63 33 : const char *auth_password = NULL;
64 33 : struct TMH_WireMethod *wm_head = NULL;
65 33 : struct TMH_WireMethod *wm_tail = NULL;
66 : const json_t *jauth;
67 : bool no_pay_delay;
68 : bool no_refund_delay;
69 : bool no_transfer_delay;
70 : bool no_rounding_interval;
71 : struct GNUNET_JSON_Specification spec[] = {
72 33 : GNUNET_JSON_spec_string ("id",
73 : (const char **) &is.id),
74 33 : GNUNET_JSON_spec_string ("name",
75 : (const char **) &is.name),
76 33 : GNUNET_JSON_spec_mark_optional (
77 : GNUNET_JSON_spec_string ("email",
78 : (const char **) &is.email),
79 : NULL),
80 33 : GNUNET_JSON_spec_mark_optional (
81 : GNUNET_JSON_spec_string ("phone_number",
82 : (const char **) &is.phone),
83 : NULL),
84 33 : GNUNET_JSON_spec_mark_optional (
85 : GNUNET_JSON_spec_string ("website",
86 : (const char **) &is.website),
87 : NULL),
88 33 : GNUNET_JSON_spec_mark_optional (
89 : GNUNET_JSON_spec_string ("logo",
90 : (const char **) &is.logo),
91 : NULL),
92 33 : GNUNET_JSON_spec_object_const ("auth",
93 : &jauth),
94 33 : GNUNET_JSON_spec_json ("address",
95 : &is.address),
96 33 : GNUNET_JSON_spec_json ("jurisdiction",
97 : &is.jurisdiction),
98 33 : GNUNET_JSON_spec_bool ("use_stefan",
99 : &is.use_stefan),
100 33 : GNUNET_JSON_spec_mark_optional (
101 : GNUNET_JSON_spec_relative_time ("default_pay_delay",
102 : &is.default_pay_delay),
103 : &no_pay_delay),
104 33 : GNUNET_JSON_spec_mark_optional (
105 : GNUNET_JSON_spec_relative_time ("default_refund_delay",
106 : &is.default_refund_delay),
107 : &no_refund_delay),
108 33 : GNUNET_JSON_spec_mark_optional (
109 : GNUNET_JSON_spec_relative_time ("default_wire_transfer_delay",
110 : &is.default_wire_transfer_delay),
111 : &no_transfer_delay),
112 33 : GNUNET_JSON_spec_mark_optional (
113 : GNUNET_JSON_spec_time_rounder_interval (
114 : "default_wire_transfer_rounding_interval",
115 : &is.default_wire_transfer_rounding_interval),
116 : &no_rounding_interval),
117 33 : GNUNET_JSON_spec_end ()
118 : };
119 :
120 : {
121 : enum GNUNET_GenericReturnValue res;
122 :
123 33 : res = TALER_MHD_parse_json_data (connection,
124 33 : hc->request_body,
125 : spec);
126 33 : if (GNUNET_OK != res)
127 : return (GNUNET_NO == res)
128 : ? MHD_YES
129 0 : : MHD_NO;
130 : }
131 33 : if (no_pay_delay)
132 0 : is.default_pay_delay = TMH_default_pay_delay;
133 33 : if (no_refund_delay)
134 14 : is.default_refund_delay = TMH_default_refund_delay;
135 33 : if (no_transfer_delay)
136 0 : is.default_wire_transfer_delay = TMH_default_wire_transfer_delay;
137 33 : if (no_rounding_interval)
138 : is.default_wire_transfer_rounding_interval
139 14 : = TMH_default_wire_transfer_rounding_interval;
140 33 : if (GNUNET_TIME_relative_is_forever (is.default_pay_delay))
141 : {
142 0 : GNUNET_break_op (0);
143 0 : GNUNET_JSON_parse_free (spec);
144 0 : return TALER_MHD_reply_with_error (connection,
145 : MHD_HTTP_BAD_REQUEST,
146 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
147 : "default_pay_delay");
148 : }
149 33 : if (GNUNET_TIME_relative_is_forever (is.default_refund_delay))
150 : {
151 0 : GNUNET_break_op (0);
152 0 : GNUNET_JSON_parse_free (spec);
153 0 : return TALER_MHD_reply_with_error (connection,
154 : MHD_HTTP_BAD_REQUEST,
155 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
156 : "default_refund_delay");
157 : }
158 33 : if (GNUNET_TIME_relative_is_forever (is.default_wire_transfer_delay))
159 : {
160 0 : GNUNET_break_op (0);
161 0 : GNUNET_JSON_parse_free (spec);
162 0 : return TALER_MHD_reply_with_error (connection,
163 : MHD_HTTP_BAD_REQUEST,
164 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
165 : "default_wire_transfer_delay");
166 : }
167 :
168 : {
169 : enum GNUNET_GenericReturnValue ret;
170 :
171 33 : ret = TMH_check_auth_config (connection,
172 : jauth,
173 : &auth_password);
174 33 : if (GNUNET_OK != ret)
175 : {
176 0 : GNUNET_JSON_parse_free (spec);
177 0 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
178 : }
179 : }
180 :
181 : /* check 'id' well-formed */
182 : {
183 : static bool once;
184 : static regex_t reg;
185 33 : bool id_wellformed = true;
186 :
187 33 : if (! once)
188 : {
189 15 : once = true;
190 15 : GNUNET_assert (0 ==
191 : regcomp (®,
192 : "^[A-Za-z0-9][A-Za-z0-9_.@-]+$",
193 : REG_EXTENDED));
194 : }
195 :
196 33 : if (0 != regexec (®,
197 33 : is.id,
198 : 0, NULL, 0))
199 0 : id_wellformed = false;
200 33 : if (! id_wellformed)
201 : {
202 0 : GNUNET_JSON_parse_free (spec);
203 0 : return TALER_MHD_reply_with_error (connection,
204 : MHD_HTTP_BAD_REQUEST,
205 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
206 : "id");
207 : }
208 : }
209 :
210 33 : if (! TMH_location_object_valid (is.address))
211 : {
212 0 : GNUNET_break_op (0);
213 0 : GNUNET_JSON_parse_free (spec);
214 0 : return TALER_MHD_reply_with_error (connection,
215 : MHD_HTTP_BAD_REQUEST,
216 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
217 : "address");
218 : }
219 :
220 33 : if (! TMH_location_object_valid (is.jurisdiction))
221 : {
222 0 : GNUNET_break_op (0);
223 0 : GNUNET_JSON_parse_free (spec);
224 0 : return TALER_MHD_reply_with_error (connection,
225 : MHD_HTTP_BAD_REQUEST,
226 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
227 : "jurisdiction");
228 : }
229 :
230 33 : if ( (NULL != is.logo) &&
231 0 : (! TMH_image_data_url_valid (is.logo)) )
232 : {
233 0 : GNUNET_break_op (0);
234 0 : GNUNET_JSON_parse_free (spec);
235 0 : return TALER_MHD_reply_with_error (connection,
236 : MHD_HTTP_BAD_REQUEST,
237 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
238 : "logo");
239 : }
240 :
241 : {
242 : /* Test if an instance of this id is known */
243 : struct TMH_MerchantInstance *mi;
244 :
245 33 : mi = TMH_lookup_instance (is.id);
246 33 : if (NULL != mi)
247 : {
248 2 : if (mi->deleted)
249 : {
250 0 : GNUNET_JSON_parse_free (spec);
251 0 : return TALER_MHD_reply_with_error (connection,
252 : MHD_HTTP_CONFLICT,
253 : TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_PURGE_REQUIRED,
254 0 : is.id);
255 : }
256 : /* Check for idempotency */
257 2 : if ( (0 == strcmp (mi->settings.id,
258 2 : is.id)) &&
259 2 : (0 == strcmp (mi->settings.name,
260 2 : is.name)) &&
261 2 : ((mi->settings.email == is.email) ||
262 0 : (NULL != is.email && NULL != mi->settings.email &&
263 0 : 0 == strcmp (mi->settings.email,
264 0 : is.email))) &&
265 2 : ((mi->settings.website == is.website) ||
266 0 : (NULL != is.website && NULL != mi->settings.website &&
267 0 : 0 == strcmp (mi->settings.website,
268 0 : is.website))) &&
269 2 : ((mi->settings.logo == is.logo) ||
270 0 : (NULL != is.logo && NULL != mi->settings.logo &&
271 0 : 0 == strcmp (mi->settings.logo,
272 0 : is.logo))) &&
273 2 : ( ( (NULL != auth_password) &&
274 : (GNUNET_OK ==
275 0 : TMH_check_auth (auth_password,
276 : &mi->auth.auth_salt,
277 2 : &mi->auth.auth_hash)) ) ||
278 4 : ( (NULL == auth_password) &&
279 : (GNUNET_YES ==
280 4 : GNUNET_is_zero (&mi->auth.auth_hash))) ) &&
281 2 : (1 == json_equal (mi->settings.address,
282 4 : is.address)) &&
283 2 : (1 == json_equal (mi->settings.jurisdiction,
284 2 : is.jurisdiction)) &&
285 2 : (mi->settings.use_stefan == is.use_stefan) &&
286 2 : (GNUNET_TIME_relative_cmp (mi->settings.default_wire_transfer_delay,
287 : ==,
288 2 : is.default_wire_transfer_delay)) &&
289 2 : (GNUNET_TIME_relative_cmp (mi->settings.default_pay_delay,
290 : ==,
291 2 : is.default_pay_delay)) &&
292 2 : (GNUNET_TIME_relative_cmp (mi->settings.default_refund_delay,
293 : ==,
294 : is.default_refund_delay)) )
295 : {
296 2 : GNUNET_JSON_parse_free (spec);
297 2 : return TALER_MHD_reply_static (connection,
298 : MHD_HTTP_NO_CONTENT,
299 : NULL,
300 : NULL,
301 : 0);
302 : }
303 0 : GNUNET_JSON_parse_free (spec);
304 0 : return TALER_MHD_reply_with_error (connection,
305 : MHD_HTTP_CONFLICT,
306 : TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS,
307 0 : is.id);
308 : }
309 : }
310 :
311 : /* Check MFA is satisfied */
312 31 : if (validation_needed)
313 : {
314 0 : enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
315 :
316 0 : if ( (0 != (TEH_TCS_SMS & TEH_mandatory_tan_channels)) &&
317 0 : (NULL == is.phone) )
318 : {
319 0 : GNUNET_break_op (0);
320 0 : GNUNET_JSON_parse_free (spec);
321 0 : return TALER_MHD_reply_with_error (connection,
322 : MHD_HTTP_BAD_REQUEST,
323 : TALER_EC_GENERIC_PARAMETER_MISSING,
324 : "phone_number");
325 :
326 : }
327 0 : if ( (0 != (TEH_TCS_EMAIL & TEH_mandatory_tan_channels)) &&
328 0 : (NULL == is.email) )
329 : {
330 0 : GNUNET_break_op (0);
331 0 : GNUNET_JSON_parse_free (spec);
332 0 : return TALER_MHD_reply_with_error (connection,
333 : MHD_HTTP_BAD_REQUEST,
334 : TALER_EC_GENERIC_PARAMETER_MISSING,
335 : "email");
336 : }
337 0 : switch (TEH_mandatory_tan_channels)
338 : {
339 0 : case TEH_TCS_NONE:
340 0 : GNUNET_assert (0);
341 : ret = GNUNET_OK;
342 : break;
343 0 : case TEH_TCS_SMS:
344 0 : is.phone_validated = true;
345 0 : ret = TMH_mfa_challenges_do (hc,
346 : TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,
347 : true,
348 : TALER_MERCHANT_MFA_CHANNEL_SMS,
349 : is.phone,
350 : TALER_MERCHANT_MFA_CHANNEL_NONE);
351 0 : break;
352 0 : case TEH_TCS_EMAIL:
353 0 : is.email_validated = true;
354 0 : ret = TMH_mfa_challenges_do (hc,
355 : TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,
356 : true,
357 : TALER_MERCHANT_MFA_CHANNEL_EMAIL,
358 : is.email,
359 : TALER_MERCHANT_MFA_CHANNEL_NONE);
360 0 : break;
361 0 : case TEH_TCS_EMAIL_AND_SMS:
362 0 : is.phone_validated = true;
363 0 : is.email_validated = true;
364 0 : ret = TMH_mfa_challenges_do (hc,
365 : TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,
366 : true,
367 : TALER_MERCHANT_MFA_CHANNEL_SMS,
368 : is.phone,
369 : TALER_MERCHANT_MFA_CHANNEL_EMAIL,
370 : is.email,
371 : TALER_MERCHANT_MFA_CHANNEL_NONE);
372 0 : break;
373 : }
374 0 : if (GNUNET_OK != ret)
375 : {
376 0 : GNUNET_JSON_parse_free (spec);
377 : return (GNUNET_NO == ret)
378 : ? MHD_YES
379 0 : : MHD_NO;
380 : }
381 : }
382 :
383 : /* handle authentication token setup */
384 31 : if (NULL == auth_password)
385 : {
386 25 : memset (&ias.auth_salt,
387 : 0,
388 : sizeof (ias.auth_salt));
389 25 : memset (&ias.auth_hash,
390 : 0,
391 : sizeof (ias.auth_hash));
392 : }
393 : else
394 : {
395 : /* Sets 'auth_salt' and 'auth_hash' */
396 6 : TMH_compute_auth (auth_password,
397 : &ias.auth_salt,
398 : &ias.auth_hash);
399 : }
400 :
401 : /* create in-memory data structure */
402 : {
403 : struct TMH_MerchantInstance *mi;
404 : enum GNUNET_DB_QueryStatus qs;
405 :
406 31 : mi = GNUNET_new (struct TMH_MerchantInstance);
407 31 : mi->wm_head = wm_head;
408 31 : mi->wm_tail = wm_tail;
409 31 : mi->settings = is;
410 31 : mi->settings.address = json_incref (mi->settings.address);
411 31 : mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
412 31 : mi->settings.id = GNUNET_strdup (is.id);
413 31 : mi->settings.name = GNUNET_strdup (is.name);
414 31 : if (NULL != is.email)
415 0 : mi->settings.email = GNUNET_strdup (is.email);
416 31 : if (NULL != is.phone)
417 0 : mi->settings.phone = GNUNET_strdup (is.phone);
418 31 : if (NULL != is.website)
419 0 : mi->settings.website = GNUNET_strdup (is.website);
420 31 : if (NULL != is.logo)
421 0 : mi->settings.logo = GNUNET_strdup (is.logo);
422 31 : mi->auth = ias;
423 31 : mi->validation_needed = validation_needed;
424 31 : GNUNET_CRYPTO_eddsa_key_create (&mi->merchant_priv.eddsa_priv);
425 31 : GNUNET_CRYPTO_eddsa_key_get_public (&mi->merchant_priv.eddsa_priv,
426 : &mi->merchant_pub.eddsa_pub);
427 :
428 31 : for (unsigned int i = 0; i<MAX_RETRIES; i++)
429 : {
430 31 : if (GNUNET_OK !=
431 31 : TMH_db->start (TMH_db->cls,
432 : "post /instances"))
433 : {
434 0 : mi->rc = 1;
435 0 : TMH_instance_decref (mi);
436 0 : GNUNET_JSON_parse_free (spec);
437 0 : return TALER_MHD_reply_with_error (connection,
438 : MHD_HTTP_INTERNAL_SERVER_ERROR,
439 : TALER_EC_GENERIC_DB_START_FAILED,
440 : NULL);
441 : }
442 31 : qs = TMH_db->insert_instance (TMH_db->cls,
443 31 : &mi->merchant_pub,
444 31 : &mi->merchant_priv,
445 31 : &mi->settings,
446 31 : &mi->auth,
447 : validation_needed);
448 31 : switch (qs)
449 : {
450 0 : case GNUNET_DB_STATUS_HARD_ERROR:
451 : {
452 : MHD_RESULT ret;
453 :
454 0 : TMH_db->rollback (TMH_db->cls);
455 0 : GNUNET_break (0);
456 0 : ret = TALER_MHD_reply_with_error (connection,
457 : MHD_HTTP_INTERNAL_SERVER_ERROR,
458 : TALER_EC_GENERIC_DB_STORE_FAILED,
459 0 : is.id);
460 0 : mi->rc = 1;
461 0 : TMH_instance_decref (mi);
462 0 : GNUNET_JSON_parse_free (spec);
463 0 : return ret;
464 : }
465 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
466 0 : goto retry;
467 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
468 : {
469 : MHD_RESULT ret;
470 :
471 0 : TMH_db->rollback (TMH_db->cls);
472 0 : GNUNET_break (0);
473 0 : ret = TALER_MHD_reply_with_error (connection,
474 : MHD_HTTP_CONFLICT,
475 : TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS,
476 0 : is.id);
477 0 : mi->rc = 1;
478 0 : TMH_instance_decref (mi);
479 0 : GNUNET_JSON_parse_free (spec);
480 0 : return ret;
481 : }
482 31 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
483 : /* handled below */
484 31 : break;
485 : }
486 31 : qs = TMH_db->commit (TMH_db->cls);
487 31 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
488 31 : qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
489 0 : retry:
490 31 : if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
491 31 : break; /* success! -- or hard failure */
492 : } /* for .. MAX_RETRIES */
493 31 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
494 : {
495 0 : mi->rc = 1;
496 0 : TMH_instance_decref (mi);
497 0 : GNUNET_JSON_parse_free (spec);
498 0 : return TALER_MHD_reply_with_error (connection,
499 : MHD_HTTP_INTERNAL_SERVER_ERROR,
500 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
501 : NULL);
502 : }
503 : /* Finally, also update our running process */
504 31 : GNUNET_assert (GNUNET_OK ==
505 : TMH_add_instance (mi));
506 31 : TMH_reload_instances (mi->settings.id);
507 : }
508 31 : GNUNET_JSON_parse_free (spec);
509 31 : if (GNUNET_TIME_relative_is_zero (login_token_expiration))
510 : {
511 31 : return TALER_MHD_reply_static (connection,
512 : MHD_HTTP_NO_CONTENT,
513 : NULL,
514 : NULL,
515 : 0);
516 : }
517 :
518 : {
519 : struct TALER_MERCHANTDB_LoginTokenP btoken;
520 0 : enum TMH_AuthScope iscope = TMH_AS_REFRESHABLE | TMH_AS_SPA;
521 : enum GNUNET_DB_QueryStatus qs;
522 : struct GNUNET_TIME_Timestamp expiration_time;
523 0 : bool refreshable = true;
524 :
525 0 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
526 : &btoken,
527 : sizeof (btoken));
528 : expiration_time
529 0 : = GNUNET_TIME_relative_to_timestamp (login_token_expiration);
530 0 : qs = TMH_db->insert_login_token (TMH_db->cls,
531 0 : is.id,
532 : &btoken,
533 : GNUNET_TIME_timestamp_get (),
534 : expiration_time,
535 : iscope,
536 : "login token from instance creation");
537 0 : switch (qs)
538 : {
539 0 : case GNUNET_DB_STATUS_HARD_ERROR:
540 : case GNUNET_DB_STATUS_SOFT_ERROR:
541 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
542 0 : GNUNET_break (0);
543 0 : return TALER_MHD_reply_with_ec (connection,
544 : TALER_EC_GENERIC_DB_STORE_FAILED,
545 : "insert_login_token");
546 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
547 0 : break;
548 : }
549 :
550 : {
551 : char *tok;
552 : MHD_RESULT ret;
553 : char *val;
554 :
555 0 : val = GNUNET_STRINGS_data_to_string_alloc (&btoken,
556 : sizeof (btoken));
557 0 : GNUNET_asprintf (&tok,
558 : RFC_8959_PREFIX "%s",
559 : val);
560 0 : GNUNET_free (val);
561 0 : ret = TALER_MHD_REPLY_JSON_PACK (
562 : connection,
563 : MHD_HTTP_OK,
564 : GNUNET_JSON_pack_string ("access_token",
565 : tok),
566 : GNUNET_JSON_pack_string ("token",
567 : tok),
568 : GNUNET_JSON_pack_string ("scope",
569 : TMH_get_name_by_scope (iscope,
570 : &refreshable)),
571 : GNUNET_JSON_pack_bool ("refreshable",
572 : refreshable),
573 : GNUNET_JSON_pack_timestamp ("expiration",
574 : expiration_time));
575 0 : GNUNET_free (tok);
576 0 : return ret;
577 : }
578 : }
579 : }
580 :
581 :
582 : /**
583 : * Generate an instance, given its configuration.
584 : *
585 : * @param rh context of the handler
586 : * @param connection the MHD connection to handle
587 : * @param[in,out] hc context with further information about the request
588 : * @return MHD result code
589 : */
590 : MHD_RESULT
591 33 : TMH_private_post_instances (const struct TMH_RequestHandler *rh,
592 : struct MHD_Connection *connection,
593 : struct TMH_HandlerContext *hc)
594 : {
595 66 : return post_instances (rh,
596 : connection,
597 : hc,
598 33 : GNUNET_TIME_UNIT_ZERO,
599 : false);
600 : }
601 :
602 :
603 : /**
604 : * Generate an instance, given its configuration.
605 : * Public handler to be used when self-provisioning.
606 : *
607 : * @param rh context of the handler
608 : * @param connection the MHD connection to handle
609 : * @param[in,out] hc context with further information about the request
610 : * @return MHD result code
611 : */
612 : MHD_RESULT
613 0 : TMH_public_post_instances (const struct TMH_RequestHandler *rh,
614 : struct MHD_Connection *connection,
615 : struct TMH_HandlerContext *hc)
616 : {
617 : struct GNUNET_TIME_Relative expiration;
618 :
619 0 : TALER_MHD_parse_request_rel_time (connection,
620 : "token_validity_ms",
621 : &expiration);
622 0 : if (GNUNET_YES !=
623 : TMH_have_self_provisioning)
624 : {
625 0 : GNUNET_break_op (0);
626 0 : return TALER_MHD_reply_with_error (connection,
627 : MHD_HTTP_FORBIDDEN,
628 : TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
629 : "Self-provisioning is not enabled");
630 : }
631 :
632 0 : return post_instances (rh,
633 : connection,
634 : hc,
635 : expiration,
636 : TEH_TCS_NONE !=
637 : TEH_mandatory_tan_channels);
638 : }
639 :
640 :
641 : /* end of taler-merchant-httpd_private-post-instances.c */
|