account_history.c (10959B)
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 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 account_history.c 18 * @brief helper function to build AML inputs from account histories 19 * @author Christian Grothoff 20 */ 21 #include "exchangedb_lib.h" 22 #include "exchangedb_lib.h" 23 #include "taler/taler_kyclogic_lib.h" 24 #include "taler/taler_json_lib.h" 25 #include "exchange-database/iterate_aml_history.h" 26 #include "exchange-database/iterate_kyc_history.h" 27 #include "exchange-database/get_kyc_rules.h" 28 #include "exchange-database/iterate_aml_attributes.h" 29 #include "exchange-database/account_history.h" 30 #include <gnunet/gnunet_common.h> 31 32 /** 33 * Function called to expand AML history for the account. 34 * 35 * @param cls a `json_t *` array to build 36 * @param outcome_serial_id row ID of the decision 37 * @param decision_time when was the decision taken 38 * @param justification what was the given justification 39 * @param decider_pub which key signed the decision 40 * @param jproperties what are the new account properties 41 * @param jnew_rules what are the new account rules 42 * @param to_investigate should AML staff investigate 43 * after the decision 44 * @param is_active is this the active decision 45 */ 46 static void 47 add_aml_history_entry ( 48 void *cls, 49 uint64_t outcome_serial_id, 50 struct GNUNET_TIME_Timestamp decision_time, 51 const char *justification, 52 const struct TALER_AmlOfficerPublicKeyP *decider_pub, 53 const json_t *jproperties, 54 const json_t *jnew_rules, 55 bool to_investigate, 56 bool is_active) 57 { 58 json_t *aml_history = cls; 59 json_t *e; 60 61 e = GNUNET_JSON_PACK ( 62 GNUNET_JSON_pack_timestamp ("decision_time", 63 decision_time), 64 GNUNET_JSON_pack_string ("justification", 65 justification), 66 GNUNET_JSON_pack_data_auto ("decider_pub", 67 decider_pub), 68 /* the column is nullable: a decision may set no properties */ 69 GNUNET_JSON_pack_allow_null ( 70 GNUNET_JSON_pack_object_incref ("properties", 71 (json_t *) jproperties)), 72 GNUNET_JSON_pack_object_incref ("new_rules", 73 (json_t *) jnew_rules), 74 GNUNET_JSON_pack_bool ("to_investigate", 75 to_investigate), 76 GNUNET_JSON_pack_bool ("is_active", 77 is_active) 78 ); 79 GNUNET_assert (0 == 80 json_array_append_new (aml_history, 81 e)); 82 } 83 84 85 json_t * 86 TALER_EXCHANGEDB_aml_history_builder (void *cls) 87 { 88 struct TALER_EXCHANGEDB_HistoryBuilderContext *hbc = cls; 89 const struct TALER_NormalizedPaytoHashP *acc = hbc->account; 90 enum GNUNET_DB_QueryStatus qs; 91 json_t *aml_history; 92 93 aml_history = json_array (); 94 GNUNET_assert (NULL != aml_history); 95 qs = TALER_EXCHANGEDB_iterate_aml_history ( 96 hbc->pg, 97 acc, 98 INT64_MAX, /* offset; note: the offset is passed to Postgres as a 99 signed INT8, so UINT64_MAX would arrive as -1 and 100 match nothing */ 101 -16 * 1024, /* limit: none for all practical purposes (for now) */ 102 &add_aml_history_entry, 103 aml_history); 104 switch (qs) 105 { 106 case GNUNET_DB_STATUS_HARD_ERROR: 107 case GNUNET_DB_STATUS_SOFT_ERROR: 108 GNUNET_break (0); 109 json_decref (aml_history); 110 return NULL; 111 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 112 /* empty history is fine! */ 113 break; 114 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 115 break; 116 } 117 return aml_history; 118 } 119 120 121 /** 122 * Closure for #add_kyc_history_entry. 123 */ 124 struct KycContext 125 { 126 /** 127 * JSON array we are building. 128 */ 129 json_t *kyc_history; 130 131 /** 132 * Key to use to decrypt KYC attributes. 133 */ 134 const struct TALER_AttributeEncryptionKeyP *attribute_key; 135 }; 136 137 138 /** 139 * Function called to expand KYC history for the account. 140 * 141 * @param cls a `json_t *` array to build 142 * @param provider_name name of the KYC provider 143 * or NULL for none 144 * @param finished did the KYC process finish 145 * @param error_code error code from the KYC process 146 * @param error_message error message from the KYC process, 147 * or NULL for none 148 * @param provider_user_id user ID at the provider 149 * or NULL for none 150 * @param provider_legitimization_id legitimization process ID at the provider 151 * or NULL for none 152 * @param collection_time when was the data collected 153 * @param expiration_time when does the collected data expire 154 * @param encrypted_attributes_len number of bytes in @a encrypted_attributes 155 * @param encrypted_attributes encrypted KYC attributes 156 */ 157 static void 158 add_kyc_history_entry ( 159 void *cls, 160 const char *provider_name, 161 bool finished, 162 enum TALER_ErrorCode error_code, 163 const char *error_message, 164 const char *provider_user_id, 165 const char *provider_legitimization_id, 166 struct GNUNET_TIME_Timestamp collection_time, 167 struct GNUNET_TIME_Absolute expiration_time, 168 size_t encrypted_attributes_len, 169 const void *encrypted_attributes) 170 { 171 struct KycContext *kc = cls; 172 json_t *kyc_history = kc->kyc_history; 173 json_t *attributes; 174 json_t *e; 175 176 attributes = TALER_CRYPTO_kyc_attributes_decrypt ( 177 kc->attribute_key, 178 encrypted_attributes, 179 encrypted_attributes_len); 180 e = GNUNET_JSON_PACK ( 181 GNUNET_JSON_pack_string ( 182 "provider_name", 183 provider_name), 184 GNUNET_JSON_pack_bool ( 185 "finished", 186 finished), 187 TALER_JSON_pack_ec (error_code), 188 GNUNET_JSON_pack_allow_null ( 189 GNUNET_JSON_pack_string ( 190 "error_message", 191 error_message)), 192 GNUNET_JSON_pack_allow_null ( 193 GNUNET_JSON_pack_string ( 194 "provider_user_id", 195 provider_user_id)), 196 GNUNET_JSON_pack_allow_null ( 197 GNUNET_JSON_pack_string ( 198 "provider_legitimization_id", 199 provider_legitimization_id)), 200 GNUNET_JSON_pack_allow_null ( 201 GNUNET_JSON_pack_timestamp ( 202 "collection_time", 203 collection_time)), 204 GNUNET_JSON_pack_allow_null ( 205 GNUNET_JSON_pack_timestamp ( 206 "expiration_time", 207 GNUNET_TIME_absolute_to_timestamp ( 208 expiration_time))), 209 GNUNET_JSON_pack_allow_null ( 210 GNUNET_JSON_pack_object_steal ( 211 "attributes", 212 attributes)) 213 ); 214 215 GNUNET_assert (0 == 216 json_array_append_new (kyc_history, 217 e)); 218 } 219 220 221 json_t * 222 TALER_EXCHANGEDB_kyc_history_builder (void *cls) 223 { 224 struct TALER_EXCHANGEDB_HistoryBuilderContext *hbc = cls; 225 const struct TALER_NormalizedPaytoHashP *acc = hbc->account; 226 enum GNUNET_DB_QueryStatus qs; 227 struct KycContext kc = { 228 .kyc_history = json_array (), 229 .attribute_key = hbc->attribute_key 230 }; 231 232 GNUNET_assert (NULL != kc.kyc_history); 233 qs = TALER_EXCHANGEDB_iterate_kyc_history ( 234 hbc->pg, 235 acc, 236 &add_kyc_history_entry, 237 &kc); 238 switch (qs) 239 { 240 case GNUNET_DB_STATUS_HARD_ERROR: 241 case GNUNET_DB_STATUS_SOFT_ERROR: 242 GNUNET_break (0); 243 json_decref (kc.kyc_history); 244 return NULL; 245 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 246 /* empty history is fine! */ 247 break; 248 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 249 break; 250 } 251 return kc.kyc_history; 252 } 253 254 255 json_t * 256 TALER_EXCHANGEDB_current_rule_builder (void *cls) 257 { 258 struct TALER_EXCHANGEDB_HistoryBuilderContext *hbc = cls; 259 const struct TALER_NormalizedPaytoHashP *acc = hbc->account; 260 enum GNUNET_DB_QueryStatus qs; 261 json_t *jlrs; 262 263 qs = TALER_EXCHANGEDB_get_kyc_rules ( 264 hbc->pg, 265 acc, 266 &jlrs); 267 switch (qs) 268 { 269 case GNUNET_DB_STATUS_HARD_ERROR: 270 case GNUNET_DB_STATUS_SOFT_ERROR: 271 GNUNET_break (0); 272 return NULL; 273 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 274 jlrs = TALER_KYCLOGIC_get_default_legi_rules ( 275 hbc->is_wallet); 276 break; 277 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 278 if (NULL == jlrs) 279 { 280 /* A NULL rule set means the account is on the exchange's default 281 rules (see exchange_do_insert_successor_measure), not that it has 282 no rules at all. Without this AML programs would be run without 283 "current_rules" and fail. */ 284 jlrs = TALER_KYCLOGIC_get_default_legi_rules ( 285 hbc->is_wallet); 286 } 287 break; 288 } 289 return jlrs; 290 } 291 292 293 /** 294 * Closure for decrypt_attributes(). 295 */ 296 struct DecryptContext 297 { 298 /** 299 * Overall context. 300 */ 301 const struct TALER_EXCHANGEDB_HistoryBuilderContext *hbc; 302 303 /** 304 * Where to return the attributes. 305 */ 306 json_t *attr; 307 }; 308 309 310 /** 311 * Decrypt and return AML attribute information. 312 * 313 * @param cls a `struct DecryptContext *` 314 * @param row_id current row in kyc_attributes table 315 * @param collection_time when were the attributes collected 316 * @param by_aml_officer true if filed by AML officer 317 * @param officer_name name of the officer, NULL if not @a by_aml_officer 318 * @param enc_attributes_size size of @a enc_attributes 319 * @param enc_attributes the encrypted collected attributes 320 */ 321 static void 322 decrypt_attributes ( 323 void *cls, 324 uint64_t row_id, 325 struct GNUNET_TIME_Timestamp collection_time, 326 bool by_aml_officer, 327 const char *officer_name, 328 size_t enc_attributes_size, 329 const void *enc_attributes) 330 { 331 struct DecryptContext *decon = cls; 332 333 (void) row_id; 334 (void) collection_time; 335 (void) officer_name; 336 decon->attr 337 = TALER_CRYPTO_kyc_attributes_decrypt (decon->hbc->attribute_key, 338 enc_attributes, 339 enc_attributes_size); 340 GNUNET_break (NULL != decon->attr); 341 } 342 343 344 json_t * 345 TALER_EXCHANGEDB_current_attributes_builder (void *cls) 346 { 347 struct TALER_EXCHANGEDB_HistoryBuilderContext *hbc = cls; 348 const struct TALER_NormalizedPaytoHashP *acc = hbc->account; 349 enum GNUNET_DB_QueryStatus qs; 350 struct DecryptContext decon = { 351 .hbc = hbc 352 }; 353 354 qs = TALER_EXCHANGEDB_iterate_aml_attributes ( 355 hbc->pg, 356 acc, 357 INT64_MAX, 358 -1, /* we only fetch the latest ones */ 359 &decrypt_attributes, 360 &decon); 361 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 362 "select_aml_attributes returned %d\n", 363 (int) qs); 364 switch (qs) 365 { 366 case GNUNET_DB_STATUS_HARD_ERROR: 367 case GNUNET_DB_STATUS_SOFT_ERROR: 368 GNUNET_break (0); 369 return NULL; 370 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 371 decon.attr = json_object (); 372 GNUNET_break (NULL != decon.attr); 373 break; 374 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 375 GNUNET_break (NULL != decon.attr); 376 break; 377 } 378 return decon.attr; 379 }