taler-exchange-httpd_management_wire_enable.c (10841B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020-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 taler-exchange-httpd_management_wire_enable.c 18 * @brief Handle request to add wire account. 19 * @author Christian Grothoff 20 */ 21 #include "taler/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/taler_json_lib.h" 28 #include "taler/taler_mhd_lib.h" 29 #include "taler/taler_signatures.h" 30 #include "taler-exchange-httpd_management.h" 31 #include "taler-exchange-httpd_responses.h" 32 #include "taler-exchange-httpd_keys.h" 33 34 35 /** 36 * Closure for the #add_wire transaction. 37 */ 38 struct AddWireContext 39 { 40 /** 41 * Master signature affirming the WIRE ADD operation 42 * (includes timestamp). 43 */ 44 struct TALER_MasterSignatureP master_sig_add; 45 46 /** 47 * Master signature to share with clients affirming the 48 * wire details of the bank. 49 */ 50 struct TALER_MasterSignatureP master_sig_wire; 51 52 /** 53 * Payto:// URI this is about. 54 */ 55 struct TALER_FullPayto payto_uri; 56 57 /** 58 * (optional) address of a conversion service for this account. 59 */ 60 const char *conversion_url; 61 62 /** 63 * (optional) address of an open banking gateway service for this account. 64 */ 65 const char *open_banking_gateway; 66 67 /** 68 * (optional) address of a wire transfer gateway service for this account. 69 */ 70 const char *wire_transfer_gateway; 71 72 /** 73 * Restrictions imposed when crediting this account. 74 */ 75 const json_t *credit_restrictions; 76 77 /** 78 * Restrictions imposed when debiting this account. 79 */ 80 const json_t *debit_restrictions; 81 82 /** 83 * Timestamp for checking against replay attacks. 84 */ 85 struct GNUNET_TIME_Timestamp validity_start; 86 87 /** 88 * Label to use for this bank. Default is empty. 89 */ 90 const char *bank_label; 91 92 /** 93 * Priority of the bank in the list. Default 0. 94 */ 95 int64_t priority; 96 97 }; 98 99 100 /** 101 * Function implementing database transaction to add an wire. Runs the 102 * transaction logic; IF it returns a non-error code, the transaction logic 103 * MUST NOT queue a MHD response. IF it returns an hard error, the 104 * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it 105 * returns the soft error code, the function MAY be called again to retry and 106 * MUST not queue a MHD response. 107 * 108 * @param cls closure with a `struct AddWireContext` 109 * @param connection MHD request which triggered the transaction 110 * @param[out] mhd_ret set to MHD response status for @a connection, 111 * if transaction failed (!) 112 * @return transaction status 113 */ 114 static enum GNUNET_DB_QueryStatus 115 add_wire (void *cls, 116 struct MHD_Connection *connection, 117 MHD_RESULT *mhd_ret) 118 { 119 struct AddWireContext *awc = cls; 120 struct GNUNET_TIME_Timestamp last_date; 121 enum GNUNET_DB_QueryStatus qs; 122 123 qs = TEH_plugin->lookup_wire_timestamp (TEH_plugin->cls, 124 awc->payto_uri, 125 &last_date); 126 if (qs < 0) 127 { 128 if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 129 return qs; 130 GNUNET_break (0); 131 *mhd_ret = TALER_MHD_reply_with_error (connection, 132 MHD_HTTP_INTERNAL_SERVER_ERROR, 133 TALER_EC_GENERIC_DB_FETCH_FAILED, 134 "lookup wire"); 135 return qs; 136 } 137 if ( (0 < qs) && 138 (GNUNET_TIME_timestamp_cmp (last_date, 139 >, 140 awc->validity_start)) ) 141 { 142 *mhd_ret = TALER_MHD_reply_with_error ( 143 connection, 144 MHD_HTTP_CONFLICT, 145 TALER_EC_EXCHANGE_MANAGEMENT_WIRE_MORE_RECENT_PRESENT, 146 NULL); 147 return GNUNET_DB_STATUS_HARD_ERROR; 148 } 149 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 150 qs = TEH_plugin->insert_wire (TEH_plugin->cls, 151 awc->payto_uri, 152 awc->conversion_url, 153 awc->open_banking_gateway, 154 awc->wire_transfer_gateway, 155 awc->debit_restrictions, 156 awc->credit_restrictions, 157 awc->validity_start, 158 &awc->master_sig_wire, 159 awc->bank_label, 160 awc->priority); 161 else 162 qs = TEH_plugin->update_wire (TEH_plugin->cls, 163 awc->payto_uri, 164 awc->conversion_url, 165 awc->open_banking_gateway, 166 awc->wire_transfer_gateway, 167 awc->debit_restrictions, 168 awc->credit_restrictions, 169 awc->validity_start, 170 &awc->master_sig_wire, 171 awc->bank_label, 172 awc->priority, 173 true); 174 if (qs < 0) 175 { 176 GNUNET_break (0); 177 if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 178 return qs; 179 *mhd_ret = TALER_MHD_reply_with_error (connection, 180 MHD_HTTP_INTERNAL_SERVER_ERROR, 181 TALER_EC_GENERIC_DB_STORE_FAILED, 182 "add wire"); 183 return qs; 184 } 185 return qs; 186 } 187 188 189 MHD_RESULT 190 TEH_handler_management_post_wire ( 191 struct MHD_Connection *connection, 192 const json_t *root) 193 { 194 struct AddWireContext awc = { 195 .conversion_url = NULL 196 }; 197 struct GNUNET_JSON_Specification spec[] = { 198 TALER_JSON_spec_full_payto_uri ("payto_uri", 199 &awc.payto_uri), 200 GNUNET_JSON_spec_fixed_auto ("master_sig_wire", 201 &awc.master_sig_wire), 202 GNUNET_JSON_spec_fixed_auto ("master_sig_add", 203 &awc.master_sig_add), 204 GNUNET_JSON_spec_timestamp ("validity_start", 205 &awc.validity_start), 206 GNUNET_JSON_spec_mark_optional ( 207 TALER_JSON_spec_web_url ("conversion_url", 208 &awc.conversion_url), 209 NULL), 210 GNUNET_JSON_spec_mark_optional ( 211 TALER_JSON_spec_web_url ("open_banking_gateway", 212 &awc.open_banking_gateway), 213 NULL), 214 GNUNET_JSON_spec_mark_optional ( 215 TALER_JSON_spec_web_url ("wire_transfer_gateway", 216 &awc.wire_transfer_gateway), 217 NULL), 218 GNUNET_JSON_spec_array_const ("credit_restrictions", 219 &awc.credit_restrictions), 220 GNUNET_JSON_spec_array_const ("debit_restrictions", 221 &awc.debit_restrictions), 222 GNUNET_JSON_spec_mark_optional ( 223 GNUNET_JSON_spec_string ("bank_label", 224 &awc.bank_label), 225 NULL), 226 GNUNET_JSON_spec_mark_optional ( 227 GNUNET_JSON_spec_int64 ("priority", 228 &awc.priority), 229 NULL), 230 GNUNET_JSON_spec_end () 231 }; 232 233 { 234 enum GNUNET_GenericReturnValue res; 235 236 res = TALER_MHD_parse_json_data (connection, 237 root, 238 spec); 239 if (GNUNET_SYSERR == res) 240 return MHD_NO; /* hard failure */ 241 if (GNUNET_NO == res) 242 return MHD_YES; /* failure */ 243 } 244 TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; 245 { 246 char *msg = TALER_payto_validate (awc.payto_uri); 247 248 if (NULL != msg) 249 { 250 MHD_RESULT ret; 251 252 GNUNET_break_op (0); 253 ret = TALER_MHD_reply_with_error ( 254 connection, 255 MHD_HTTP_BAD_REQUEST, 256 TALER_EC_GENERIC_PAYTO_URI_MALFORMED, 257 msg); 258 GNUNET_JSON_parse_free (spec); 259 GNUNET_free (msg); 260 return ret; 261 } 262 } 263 if (GNUNET_OK != 264 TALER_exchange_offline_wire_add_verify ( 265 awc.payto_uri, 266 awc.conversion_url, 267 awc.open_banking_gateway, 268 awc.wire_transfer_gateway, 269 awc.debit_restrictions, 270 awc.credit_restrictions, 271 awc.validity_start, 272 &TEH_master_public_key, 273 &awc.master_sig_add)) 274 { 275 GNUNET_break_op (0); 276 GNUNET_JSON_parse_free (spec); 277 return TALER_MHD_reply_with_error ( 278 connection, 279 MHD_HTTP_FORBIDDEN, 280 TALER_EC_EXCHANGE_MANAGEMENT_WIRE_ADD_SIGNATURE_INVALID, 281 NULL); 282 } 283 TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; 284 if (GNUNET_OK != 285 TALER_exchange_wire_signature_check ( 286 awc.payto_uri, 287 awc.conversion_url, 288 awc.open_banking_gateway, 289 awc.wire_transfer_gateway, 290 awc.debit_restrictions, 291 awc.credit_restrictions, 292 &TEH_master_public_key, 293 &awc.master_sig_wire)) 294 { 295 GNUNET_break_op (0); 296 GNUNET_JSON_parse_free (spec); 297 return TALER_MHD_reply_with_error ( 298 connection, 299 MHD_HTTP_FORBIDDEN, 300 TALER_EC_EXCHANGE_MANAGEMENT_WIRE_DETAILS_SIGNATURE_INVALID, 301 NULL); 302 } 303 { 304 char *wire_method; 305 306 wire_method = TALER_payto_get_method (awc.payto_uri.full_payto); 307 if (NULL == wire_method) 308 { 309 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 310 "payto:// URI `%s' is malformed\n", 311 awc.payto_uri.full_payto); 312 GNUNET_JSON_parse_free (spec); 313 return TALER_MHD_reply_with_error ( 314 connection, 315 MHD_HTTP_BAD_REQUEST, 316 TALER_EC_GENERIC_PARAMETER_MALFORMED, 317 "payto_uri"); 318 } 319 GNUNET_free (wire_method); 320 } 321 322 { 323 enum GNUNET_GenericReturnValue res; 324 MHD_RESULT ret; 325 326 res = TEH_DB_run_transaction (connection, 327 "add wire", 328 TEH_MT_REQUEST_OTHER, 329 &ret, 330 &add_wire, 331 &awc); 332 GNUNET_JSON_parse_free (spec); 333 if (GNUNET_SYSERR == res) 334 return ret; 335 } 336 TEH_wire_update_state (); 337 return TALER_MHD_reply_static ( 338 connection, 339 MHD_HTTP_NO_CONTENT, 340 NULL, 341 NULL, 342 0); 343 } 344 345 346 /* end of taler-exchange-httpd_management_wire.c */