Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2021-2022 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
12 :
13 : You should have received a copy of the GNU Affero General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file taler-exchange-httpd_kyc-proof.c
18 : * @brief Handle request for proof for KYC check.
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include <gnunet/gnunet_util_lib.h>
23 : #include <gnunet/gnunet_json_lib.h>
24 : #include <jansson.h>
25 : #include <microhttpd.h>
26 : #include <pthread.h>
27 : #include "taler_json_lib.h"
28 : #include "taler_kyclogic_lib.h"
29 : #include "taler_mhd_lib.h"
30 : #include "taler-exchange-httpd_kyc-proof.h"
31 : #include "taler-exchange-httpd_responses.h"
32 :
33 :
34 : /**
35 : * Context for the proof.
36 : */
37 : struct KycProofContext
38 : {
39 :
40 : /**
41 : * Kept in a DLL while suspended.
42 : */
43 : struct KycProofContext *next;
44 :
45 : /**
46 : * Kept in a DLL while suspended.
47 : */
48 : struct KycProofContext *prev;
49 :
50 : /**
51 : * Details about the connection we are processing.
52 : */
53 : struct TEH_RequestContext *rc;
54 :
55 : /**
56 : * Proof logic to run.
57 : */
58 : struct TALER_KYCLOGIC_Plugin *logic;
59 :
60 : /**
61 : * Configuration for @a logic.
62 : */
63 : struct TALER_KYCLOGIC_ProviderDetails *pd;
64 :
65 : /**
66 : * Asynchronous operation with the proof system.
67 : */
68 : struct TALER_KYCLOGIC_ProofHandle *ph;
69 :
70 : /**
71 : * Process information about the user for the plugin from the database, can
72 : * be NULL.
73 : */
74 : char *provider_user_id;
75 :
76 : /**
77 : * Process information about the legitimization process for the plugin from the
78 : * database, can be NULL.
79 : */
80 : char *provider_legitimization_id;
81 :
82 : /**
83 : * Hash of payment target URI this is about.
84 : */
85 : struct TALER_PaytoHashP h_payto;
86 :
87 : /**
88 : * HTTP response to return.
89 : */
90 : struct MHD_Response *response;
91 :
92 : /**
93 : * Provider configuration section name of the logic we are running.
94 : */
95 : const char *provider_section;
96 :
97 : /**
98 : * Row in the database for this legitimization operation.
99 : */
100 : uint64_t process_row;
101 :
102 : /**
103 : * HTTP response code to return.
104 : */
105 : unsigned int response_code;
106 :
107 : /**
108 : * True if we are suspended,
109 : */
110 : bool suspended;
111 :
112 : };
113 :
114 :
115 : /**
116 : * Contexts are kept in a DLL while suspended.
117 : */
118 : static struct KycProofContext *kpc_head;
119 :
120 : /**
121 : * Contexts are kept in a DLL while suspended.
122 : */
123 : static struct KycProofContext *kpc_tail;
124 :
125 :
126 : /**
127 : * Resume processing the @a kpc request.
128 : *
129 : * @param kpc request to resume
130 : */
131 : static void
132 0 : kpc_resume (struct KycProofContext *kpc)
133 : {
134 0 : GNUNET_assert (GNUNET_YES == kpc->suspended);
135 0 : kpc->suspended = false;
136 0 : GNUNET_CONTAINER_DLL_remove (kpc_head,
137 : kpc_tail,
138 : kpc);
139 0 : MHD_resume_connection (kpc->rc->connection);
140 0 : TALER_MHD_daemon_trigger ();
141 0 : }
142 :
143 :
144 : void
145 0 : TEH_kyc_proof_cleanup (void)
146 : {
147 : struct KycProofContext *kpc;
148 :
149 0 : while (NULL != (kpc = kpc_head))
150 : {
151 0 : if (NULL != kpc->ph)
152 : {
153 0 : kpc->logic->proof_cancel (kpc->ph);
154 0 : kpc->ph = NULL;
155 : }
156 0 : kpc_resume (kpc);
157 : }
158 0 : }
159 :
160 :
161 : /**
162 : * Function called with the result of a proof check operation.
163 : *
164 : * Note that the "decref" for the @a response
165 : * will be done by the callee and MUST NOT be done by the plugin.
166 : *
167 : * @param cls closure
168 : * @param status KYC status
169 : * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown
170 : * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
171 : * @param expiration until when is the KYC check valid
172 : * @param http_status HTTP status code of @a response
173 : * @param[in] response to return to the HTTP client
174 : */
175 : static void
176 0 : proof_cb (
177 : void *cls,
178 : enum TALER_KYCLOGIC_KycStatus status,
179 : const char *provider_user_id,
180 : const char *provider_legitimization_id,
181 : struct GNUNET_TIME_Absolute expiration,
182 : unsigned int http_status,
183 : struct MHD_Response *response)
184 : {
185 0 : struct KycProofContext *kpc = cls;
186 0 : struct TEH_RequestContext *rc = kpc->rc;
187 : struct GNUNET_AsyncScopeSave old_scope;
188 :
189 0 : kpc->ph = NULL;
190 0 : GNUNET_async_scope_enter (&rc->async_scope_id,
191 : &old_scope);
192 :
193 0 : if (TALER_KYCLOGIC_STATUS_SUCCESS == status)
194 : {
195 : enum GNUNET_DB_QueryStatus qs;
196 :
197 0 : qs = TEH_plugin->update_kyc_process_by_row (TEH_plugin->cls,
198 : kpc->process_row,
199 : kpc->provider_section,
200 0 : &kpc->h_payto,
201 : provider_user_id,
202 : provider_legitimization_id,
203 : expiration);
204 0 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
205 : {
206 0 : GNUNET_break (0);
207 0 : if (NULL != response)
208 0 : MHD_destroy_response (response);
209 0 : kpc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
210 0 : kpc->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
211 : "set_kyc_ok");
212 0 : GNUNET_async_scope_restore (&old_scope);
213 0 : return;
214 : }
215 : }
216 : else
217 : {
218 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
219 : "KYC process #%llu failed with status %d\n",
220 : (unsigned long long) kpc->process_row,
221 : status);
222 : }
223 0 : kpc->response_code = http_status;
224 0 : kpc->response = response;
225 0 : kpc_resume (kpc);
226 0 : GNUNET_async_scope_restore (&old_scope);
227 : }
228 :
229 :
230 : /**
231 : * Function called to clean up a context.
232 : *
233 : * @param rc request context
234 : */
235 : static void
236 0 : clean_kpc (struct TEH_RequestContext *rc)
237 : {
238 0 : struct KycProofContext *kpc = rc->rh_ctx;
239 :
240 0 : if (NULL != kpc->ph)
241 : {
242 0 : kpc->logic->proof_cancel (kpc->ph);
243 0 : kpc->ph = NULL;
244 : }
245 0 : if (NULL != kpc->response)
246 : {
247 0 : MHD_destroy_response (kpc->response);
248 0 : kpc->response = NULL;
249 : }
250 0 : GNUNET_free (kpc->provider_user_id);
251 0 : GNUNET_free (kpc->provider_legitimization_id);
252 0 : GNUNET_free (kpc);
253 0 : }
254 :
255 :
256 : MHD_RESULT
257 0 : TEH_handler_kyc_proof (
258 : struct TEH_RequestContext *rc,
259 : const char *const args[3])
260 : {
261 0 : struct KycProofContext *kpc = rc->rh_ctx;
262 :
263 0 : if (NULL == kpc)
264 : {
265 : /* first time */
266 0 : if ( (NULL == args[0]) ||
267 0 : (NULL == args[1]) )
268 : {
269 0 : GNUNET_break_op (0);
270 0 : return TALER_MHD_reply_with_error (rc->connection,
271 : MHD_HTTP_NOT_FOUND,
272 : TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
273 : "'/kyc-proof/$H_PATYO/$LOGIC' required");
274 : }
275 :
276 0 : kpc = GNUNET_new (struct KycProofContext);
277 0 : kpc->rc = rc;
278 0 : rc->rh_ctx = kpc;
279 0 : rc->rh_cleaner = &clean_kpc;
280 0 : if (GNUNET_OK !=
281 0 : GNUNET_STRINGS_string_to_data (args[0],
282 : strlen (args[0]),
283 0 : &kpc->h_payto,
284 : sizeof (kpc->h_payto)))
285 : {
286 0 : GNUNET_break_op (0);
287 0 : return TALER_MHD_reply_with_error (rc->connection,
288 : MHD_HTTP_BAD_REQUEST,
289 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
290 : "h_payto");
291 : }
292 0 : if (GNUNET_OK !=
293 0 : TALER_KYCLOGIC_lookup_logic (args[1],
294 : &kpc->logic,
295 : &kpc->pd,
296 : &kpc->provider_section))
297 : {
298 0 : GNUNET_break_op (0);
299 0 : return TALER_MHD_reply_with_error (rc->connection,
300 : MHD_HTTP_NOT_FOUND,
301 : TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
302 0 : args[1]);
303 : }
304 0 : if (0 != strcmp (args[1],
305 : kpc->provider_section))
306 : {
307 0 : GNUNET_break_op (0);
308 0 : return TALER_MHD_reply_with_error (rc->connection,
309 : MHD_HTTP_BAD_REQUEST,
310 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
311 : "PROVIDER_SECTION");
312 : }
313 :
314 0 : if (NULL != kpc->provider_section)
315 : {
316 : enum GNUNET_DB_QueryStatus qs;
317 : struct GNUNET_TIME_Absolute expiration;
318 :
319 0 : qs = TEH_plugin->lookup_kyc_process_by_account (
320 0 : TEH_plugin->cls,
321 : kpc->provider_section,
322 0 : &kpc->h_payto,
323 : &kpc->process_row,
324 : &expiration,
325 : &kpc->provider_user_id,
326 : &kpc->provider_legitimization_id);
327 0 : switch (qs)
328 : {
329 0 : case GNUNET_DB_STATUS_HARD_ERROR:
330 : case GNUNET_DB_STATUS_SOFT_ERROR:
331 0 : return TALER_MHD_reply_with_ec (rc->connection,
332 : TALER_EC_GENERIC_DB_STORE_FAILED,
333 : "lookup_kyc_requirement_by_account");
334 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
335 0 : return TALER_MHD_reply_with_error (rc->connection,
336 : MHD_HTTP_NOT_FOUND,
337 : TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
338 : kpc->provider_section);
339 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
340 0 : break;
341 : }
342 0 : if (GNUNET_TIME_absolute_is_future (expiration))
343 : {
344 : /* KYC not required */
345 0 : return TALER_MHD_reply_static (
346 : rc->connection,
347 : MHD_HTTP_NO_CONTENT,
348 : NULL,
349 : NULL,
350 : 0);
351 : }
352 : }
353 0 : kpc->ph = kpc->logic->proof (kpc->logic->cls,
354 0 : kpc->pd,
355 : &args[2],
356 : rc->connection,
357 0 : &kpc->h_payto,
358 : kpc->process_row,
359 0 : kpc->provider_user_id,
360 0 : kpc->provider_legitimization_id,
361 : &proof_cb,
362 : kpc);
363 0 : if (NULL == kpc->ph)
364 : {
365 0 : GNUNET_break (0);
366 0 : return TALER_MHD_reply_with_error (rc->connection,
367 : MHD_HTTP_INTERNAL_SERVER_ERROR,
368 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
369 : "could not start proof with KYC logic");
370 : }
371 :
372 :
373 0 : kpc->suspended = true;
374 0 : GNUNET_CONTAINER_DLL_insert (kpc_head,
375 : kpc_tail,
376 : kpc);
377 0 : MHD_suspend_connection (rc->connection);
378 0 : return MHD_YES;
379 : }
380 :
381 0 : if (NULL == kpc->response)
382 : {
383 0 : GNUNET_break (0);
384 0 : return TALER_MHD_reply_with_error (rc->connection,
385 : MHD_HTTP_INTERNAL_SERVER_ERROR,
386 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
387 : "handler resumed without response");
388 : }
389 :
390 : /* return response from KYC logic */
391 0 : return MHD_queue_response (rc->connection,
392 : kpc->response_code,
393 : kpc->response);
394 : }
395 :
396 :
397 : /* end of taler-exchange-httpd_kyc-proof.c */
|