summaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_common_kyc.c
blob: bcee5a0d2c988c04b45e92d28f2b952eac07a73b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*
  This file is part of TALER
  Copyright (C) 2023 Taler Systems SA

  TALER is free software; you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free Software
  Foundation; either version 3, or (at your option) any later version.

  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License along with
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
*/
/**
 * @file taler-exchange-httpd_common_kyc.c
 * @brief shared logic for finishing a KYC process
 * @author Christian Grothoff
 */
#include "platform.h"
#include "taler-exchange-httpd.h"
#include "taler-exchange-httpd_common_kyc.h"
#include "taler_attributes.h"
#include "taler_error_codes.h"
#include "taler_kyclogic_lib.h"
#include "taler_exchangedb_plugin.h"
#include <gnunet/gnunet_common.h>

struct TEH_KycAmlTrigger
{

  /**
   * Our logging scope.
   */
  struct GNUNET_AsyncScopeId scope;

  /**
   * account the operation is about
   */
  struct TALER_PaytoHashP account_id;

  /**
   * until when is the KYC data valid
   */
  struct GNUNET_TIME_Absolute expiration;

  /**
   * legitimization process the KYC data is about
   */
  uint64_t process_row;

  /**
   * name of the configuration section of the logic that was run
   */
  char *provider_section;

  /**
   * set to user ID at the provider, or NULL if not supported or unknown
   */
  char *provider_user_id;

  /**
   * provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
   */
  char *provider_legitimization_id;

  /**
   * function to call with the result
   */
  TEH_KycAmlTriggerCallback cb;

  /**
   * closure for @e cb
   */
  void *cb_cls;

  /**
   * user attributes returned by the provider
   */
  json_t *attributes;

  /**
   * response to return to the HTTP client
   */
  struct MHD_Response *response;

  /**
   * Handle to an external process that evaluates the
   * need to run AML on the account.
   */
  struct TALER_JSON_ExternalConversion *kyc_aml;

  /**
   * HTTP status code of @e response
   */
  unsigned int http_status;

};


/**
 * Type of a callback that receives a JSON @a result.
 *
 * @param cls closure of type `struct TEH_KycAmlTrigger *`
 * @param status_type how did the process die
 * @param code termination status code from the process,
 *        non-zero if AML checks are required next
 * @param result some JSON result, NULL if we failed to get an JSON output
 */
static void
kyc_aml_finished (void *cls,
                  enum GNUNET_OS_ProcessStatusType status_type,
                  unsigned long code,
                  const json_t *result)
{
  struct TEH_KycAmlTrigger *kat = cls;
  enum GNUNET_DB_QueryStatus qs;
  size_t eas;
  void *ea;
  const char *birthdate;
  unsigned int birthday = 0;
  struct GNUNET_ShortHashCode kyc_prox;
  struct GNUNET_AsyncScopeSave old_scope;
  unsigned int num_checks;
  char **provided_checks;

  kat->kyc_aml = NULL;
  GNUNET_async_scope_enter (&kat->scope,
                            &old_scope);
  TALER_CRYPTO_attributes_to_kyc_prox (kat->attributes,
                                       &kyc_prox);
  birthdate = json_string_value (json_object_get (kat->attributes,
                                                  TALER_ATTRIBUTE_BIRTHDATE));
  if ( (TEH_age_restriction_enabled) &&
       (NULL != birthdate) )
  {
    enum GNUNET_GenericReturnValue ret;

    ret = TALER_parse_coarse_date (birthdate,
                                   &TEH_age_restriction_config.mask,
                                   &birthday);

    if (GNUNET_OK != ret)
    {
      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                  "Failed to parse birthdate `%s' from KYC attributes\n",
                  birthdate);
      if (NULL != kat->response)
        MHD_destroy_response (kat->response);
      kat->http_status = MHD_HTTP_BAD_REQUEST;
      kat->response = TALER_MHD_make_error (
        TALER_EC_GENERIC_PARAMETER_MALFORMED,
        TALER_ATTRIBUTE_BIRTHDATE);
      goto RETURN_RESULT;
    }
  }

  TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key,
                                       kat->attributes,
                                       &ea,
                                       &eas);
  TALER_KYCLOGIC_lookup_checks (kat->provider_section,
                                &num_checks,
                                &provided_checks);
  qs = TEH_plugin->insert_kyc_attributes (
    TEH_plugin->cls,
    kat->process_row,
    &kat->account_id,
    &kyc_prox,
    kat->provider_section,
    num_checks,
    (const char **) provided_checks,
    birthday,
    GNUNET_TIME_timestamp_get (),
    kat->provider_user_id,
    kat->provider_legitimization_id,
    kat->expiration,
    eas,
    ea,
    0 != code);
  for (unsigned int i = 0; i<num_checks; i++)
    GNUNET_free (provided_checks[i]);
  GNUNET_free (provided_checks);
  GNUNET_free (ea);
  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              "Stored encrypted KYC process #%llu attributes: %d\n",
              (unsigned long long) kat->process_row,
              qs);
  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
  {
    GNUNET_break (0);
    if (NULL != kat->response)
      MHD_destroy_response (kat->response);
    kat->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
    kat->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
                                          "do_insert_kyc_attributes");
    /* Continued below to return the response */
  }
RETURN_RESULT:
  /* Finally, return result to main handler */
  kat->cb (kat->cb_cls,
           kat->http_status,
           kat->response);
  kat->response = NULL;
  TEH_kyc_finished_cancel (kat);
  GNUNET_async_scope_restore (&old_scope);
}


struct TEH_KycAmlTrigger *
TEH_kyc_finished (const struct GNUNET_AsyncScopeId *scope,
                  uint64_t process_row,
                  const struct TALER_PaytoHashP *account_id,
                  const char *provider_section,
                  const char *provider_user_id,
                  const char *provider_legitimization_id,
                  struct GNUNET_TIME_Absolute expiration,
                  const json_t *attributes,
                  unsigned int http_status,
                  struct MHD_Response *response,
                  TEH_KycAmlTriggerCallback cb,
                  void *cb_cls)
{
  struct TEH_KycAmlTrigger *kat;

  kat = GNUNET_new (struct TEH_KycAmlTrigger);
  kat->scope = *scope;
  kat->process_row = process_row;
  kat->account_id = *account_id;
  kat->provider_section
    = GNUNET_strdup (provider_section);
  if (NULL != provider_user_id)
    kat->provider_user_id
      = GNUNET_strdup (provider_user_id);
  if (NULL != provider_legitimization_id)
    kat->provider_legitimization_id
      = GNUNET_strdup (provider_legitimization_id);
  kat->expiration = expiration;
  kat->attributes = json_incref ((json_t*) attributes);
  kat->http_status = http_status;
  kat->response = response;
  kat->cb = cb;
  kat->cb_cls = cb_cls;
  kat->kyc_aml
    = TALER_JSON_external_conversion_start (
        attributes,
        &kyc_aml_finished,
        kat,
        TEH_kyc_aml_trigger,
        TEH_kyc_aml_trigger,
        NULL);
  if (NULL == kat->kyc_aml)
  {
    GNUNET_break (0);
    TEH_kyc_finished_cancel (kat);
    return NULL;
  }
  return kat;
}


void
TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat)
{
  if (NULL != kat->kyc_aml)
  {
    TALER_JSON_external_conversion_stop (kat->kyc_aml);
    kat->kyc_aml = NULL;
  }
  GNUNET_free (kat->provider_section);
  GNUNET_free (kat->provider_user_id);
  GNUNET_free (kat->provider_legitimization_id);
  json_decref (kat->attributes);
  if (NULL != kat->response)
  {
    MHD_destroy_response (kat->response);
    kat->response = NULL;
  }
  GNUNET_free (kat);
}


bool
TEH_kyc_failed (uint64_t process_row,
                const struct TALER_PaytoHashP *account_id,
                const char *provider_section,
                const char *provider_user_id,
                const char *provider_legitimization_id)
{
  enum GNUNET_DB_QueryStatus qs;

  qs = TEH_plugin->insert_kyc_failure (
    TEH_plugin->cls,
    process_row,
    account_id,
    provider_section,
    provider_user_id,
    provider_legitimization_id);
  GNUNET_break (qs >= 0);
  return qs >= 0;
}