exchange_api_add_aml_decision.c (10809B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2023, 2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU 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 General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file lib/exchange_api_add_aml_decision.c 19 * @brief functions to add an AML decision by an AML officer 20 * @author Christian Grothoff 21 */ 22 #include "taler/platform.h" 23 #include "taler/taler_json_lib.h" 24 #include <microhttpd.h> 25 #include <gnunet/gnunet_curl_lib.h> 26 #include "taler/taler_exchange_service.h" 27 #include "exchange_api_curl_defaults.h" 28 #include "taler/taler_signatures.h" 29 #include "taler/taler_curl_lib.h" 30 #include "taler/taler_json_lib.h" 31 32 33 struct TALER_EXCHANGE_AddAmlDecision 34 { 35 36 /** 37 * The url for this request. 38 */ 39 char *url; 40 41 /** 42 * Minor context that holds body and headers. 43 */ 44 struct TALER_CURL_PostContext post_ctx; 45 46 /** 47 * Handle for the request. 48 */ 49 struct GNUNET_CURL_Job *job; 50 51 /** 52 * Function to call with the result. 53 */ 54 TALER_EXCHANGE_AddAmlDecisionCallback cb; 55 56 /** 57 * Closure for @a cb. 58 */ 59 void *cb_cls; 60 61 /** 62 * Reference to the execution context. 63 */ 64 struct GNUNET_CURL_Context *ctx; 65 }; 66 67 68 /** 69 * Function called when we're done processing the 70 * HTTP POST /aml/$OFFICER_PUB/decision request. 71 * 72 * @param cls the `struct TALER_EXCHANGE_AddAmlDecision *` 73 * @param response_code HTTP response code, 0 on error 74 * @param response response body, NULL if not in JSON 75 */ 76 static void 77 handle_add_aml_decision_finished (void *cls, 78 long response_code, 79 const void *response) 80 { 81 struct TALER_EXCHANGE_AddAmlDecision *wh = cls; 82 const json_t *json = response; 83 struct TALER_EXCHANGE_AddAmlDecisionResponse adr = { 84 .hr.http_status = (unsigned int) response_code, 85 .hr.reply = json 86 }; 87 88 wh->job = NULL; 89 switch (response_code) 90 { 91 case 0: 92 /* no reply */ 93 adr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 94 adr.hr.hint = "server offline?"; 95 break; 96 case MHD_HTTP_NO_CONTENT: 97 break; 98 case MHD_HTTP_FORBIDDEN: 99 adr.hr.ec = TALER_JSON_get_error_code (json); 100 adr.hr.hint = TALER_JSON_get_error_hint (json); 101 break; 102 case MHD_HTTP_CONFLICT: 103 adr.hr.ec = TALER_JSON_get_error_code (json); 104 adr.hr.hint = TALER_JSON_get_error_hint (json); 105 break; 106 default: 107 /* unexpected response code */ 108 GNUNET_break_op (0); 109 adr.hr.ec = TALER_JSON_get_error_code (json); 110 adr.hr.hint = TALER_JSON_get_error_hint (json); 111 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 112 "Unexpected response code %u/%d for exchange AML decision\n", 113 (unsigned int) response_code, 114 (int) adr.hr.ec); 115 break; 116 } 117 if (NULL != wh->cb) 118 { 119 wh->cb (wh->cb_cls, 120 &adr); 121 wh->cb = NULL; 122 } 123 TALER_EXCHANGE_post_aml_decision_cancel (wh); 124 } 125 126 127 struct TALER_EXCHANGE_AddAmlDecision * 128 TALER_EXCHANGE_post_aml_decision ( 129 struct GNUNET_CURL_Context *ctx, 130 const char *url, 131 const struct TALER_NormalizedPaytoHashP *h_payto, 132 const struct TALER_FullPayto payto_uri, 133 struct GNUNET_TIME_Timestamp decision_time, 134 const char *successor_measure, 135 const char *new_measures, 136 struct GNUNET_TIME_Timestamp expiration_time, 137 unsigned int num_rules, 138 const struct TALER_EXCHANGE_AccountRule *rules, 139 unsigned int num_measures, 140 const struct TALER_EXCHANGE_MeasureInformation *measures, 141 const json_t *properties, 142 bool keep_investigating, 143 const char *justification, 144 const struct TALER_AmlOfficerPrivateKeyP *officer_priv, 145 unsigned int num_events, 146 const char **events, 147 TALER_EXCHANGE_AddAmlDecisionCallback cb, 148 void *cb_cls) 149 { 150 struct TALER_AmlOfficerPublicKeyP officer_pub; 151 struct TALER_AmlOfficerSignatureP officer_sig; 152 struct TALER_EXCHANGE_AddAmlDecision *wh; 153 CURL *eh; 154 json_t *body; 155 json_t *new_rules; 156 json_t *jrules; 157 json_t *jmeasures; 158 json_t *jevents = NULL; 159 160 if (0 != num_events) 161 { 162 jevents = json_array (); 163 GNUNET_assert (NULL != jevents); 164 for (unsigned int i = 0; i<num_events; i++) 165 GNUNET_assert (0 == 166 json_array_append_new (jevents, 167 json_string (events[i]))); 168 } 169 jrules = json_array (); 170 GNUNET_assert (NULL != jrules); 171 for (unsigned int i = 0; i<num_rules; i++) 172 { 173 const struct TALER_EXCHANGE_AccountRule *al = &rules[i]; 174 json_t *rule; 175 json_t *ameasures; 176 177 ameasures = json_array (); 178 GNUNET_assert (NULL != ameasures); 179 for (unsigned int j = 0; j<al->num_measures; j++) 180 GNUNET_assert (0 == 181 json_array_append_new (ameasures, 182 json_string (al->measures[j]))); 183 rule = GNUNET_JSON_PACK ( 184 TALER_JSON_pack_kycte ("operation_type", 185 al->operation_type), 186 TALER_JSON_pack_amount ("threshold", 187 &al->threshold), 188 GNUNET_JSON_pack_time_rel ("timeframe", 189 al->timeframe), 190 GNUNET_JSON_pack_array_steal ("measures", 191 ameasures), 192 GNUNET_JSON_pack_allow_null ( 193 GNUNET_JSON_pack_array_steal ("events", 194 jevents)), 195 GNUNET_JSON_pack_bool ("exposed", 196 al->exposed), 197 GNUNET_JSON_pack_bool ("is_and_combinator", 198 al->is_and_combinator), 199 GNUNET_JSON_pack_uint64 ("display_priority", 200 al->display_priority) 201 ); 202 GNUNET_break (0 == 203 json_array_append_new (jrules, 204 rule)); 205 } 206 207 jmeasures = json_object (); 208 GNUNET_assert (NULL != jmeasures); 209 for (unsigned int i = 0; i<num_measures; i++) 210 { 211 const struct TALER_EXCHANGE_MeasureInformation *mi = &measures[i]; 212 json_t *measure; 213 214 measure = GNUNET_JSON_PACK ( 215 GNUNET_JSON_pack_string ("check_name", 216 mi->check_name), 217 GNUNET_JSON_pack_allow_null ( 218 GNUNET_JSON_pack_string ("prog_name", 219 mi->prog_name)), 220 GNUNET_JSON_pack_allow_null ( 221 GNUNET_JSON_pack_object_incref ("context", 222 (json_t *) mi->context)) 223 ); 224 GNUNET_break (0 == 225 json_object_set_new (jmeasures, 226 mi->measure_name, 227 measure)); 228 } 229 230 new_rules = GNUNET_JSON_PACK ( 231 GNUNET_JSON_pack_timestamp ("expiration_time", 232 expiration_time), 233 GNUNET_JSON_pack_allow_null ( 234 GNUNET_JSON_pack_string ("successor_measure", 235 successor_measure)), 236 GNUNET_JSON_pack_array_steal ("rules", 237 jrules), 238 GNUNET_JSON_pack_object_steal ("custom_measures", 239 jmeasures) 240 ); 241 242 GNUNET_CRYPTO_eddsa_key_get_public ( 243 &officer_priv->eddsa_priv, 244 &officer_pub.eddsa_pub); 245 TALER_officer_aml_decision_sign (justification, 246 decision_time, 247 h_payto, 248 new_rules, 249 properties, 250 new_measures, 251 keep_investigating, 252 officer_priv, 253 &officer_sig); 254 wh = GNUNET_new (struct TALER_EXCHANGE_AddAmlDecision); 255 wh->cb = cb; 256 wh->cb_cls = cb_cls; 257 wh->ctx = ctx; 258 { 259 char *path; 260 char opus[sizeof (officer_pub) * 2]; 261 char *end; 262 263 end = GNUNET_STRINGS_data_to_string ( 264 &officer_pub, 265 sizeof (officer_pub), 266 opus, 267 sizeof (opus)); 268 *end = '\0'; 269 GNUNET_asprintf (&path, 270 "aml/%s/decision", 271 opus); 272 wh->url = TALER_url_join (url, 273 path, 274 NULL); 275 GNUNET_free (path); 276 } 277 if (NULL == wh->url) 278 { 279 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 280 "Could not construct request URL.\n"); 281 GNUNET_free (wh); 282 json_decref (new_rules); 283 return NULL; 284 } 285 body = GNUNET_JSON_PACK ( 286 GNUNET_JSON_pack_string ("justification", 287 justification), 288 GNUNET_JSON_pack_data_auto ("h_payto", 289 h_payto), 290 GNUNET_JSON_pack_allow_null ( 291 TALER_JSON_pack_full_payto ("payto_uri", 292 payto_uri)), 293 GNUNET_JSON_pack_object_steal ("new_rules", 294 new_rules), 295 GNUNET_JSON_pack_object_incref ("properties", 296 (json_t *) properties), 297 GNUNET_JSON_pack_allow_null ( 298 GNUNET_JSON_pack_string ("new_measures", 299 new_measures)), 300 GNUNET_JSON_pack_bool ("keep_investigating", 301 keep_investigating), 302 GNUNET_JSON_pack_data_auto ("officer_sig", 303 &officer_sig), 304 GNUNET_JSON_pack_timestamp ("decision_time", 305 decision_time)); 306 eh = TALER_EXCHANGE_curl_easy_get_ (wh->url); 307 if ( (NULL == eh) || 308 (GNUNET_OK != 309 TALER_curl_easy_post (&wh->post_ctx, 310 eh, 311 body)) ) 312 { 313 GNUNET_break (0); 314 if (NULL != eh) 315 curl_easy_cleanup (eh); 316 json_decref (body); 317 GNUNET_free (wh->url); 318 return NULL; 319 } 320 json_decref (body); 321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 322 "Requesting URL '%s'\n", 323 wh->url); 324 wh->job = GNUNET_CURL_job_add2 (ctx, 325 eh, 326 wh->post_ctx.headers, 327 &handle_add_aml_decision_finished, 328 wh); 329 if (NULL == wh->job) 330 { 331 TALER_EXCHANGE_post_aml_decision_cancel (wh); 332 return NULL; 333 } 334 return wh; 335 } 336 337 338 void 339 TALER_EXCHANGE_post_aml_decision_cancel ( 340 struct TALER_EXCHANGE_AddAmlDecision *wh) 341 { 342 if (NULL != wh->job) 343 { 344 GNUNET_CURL_job_cancel (wh->job); 345 wh->job = NULL; 346 } 347 TALER_curl_easy_post_finished (&wh->post_ctx); 348 GNUNET_free (wh->url); 349 GNUNET_free (wh); 350 }