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