taler-merchant-httpd_private-post-account.c (11656B)
1 /* 2 This file is part of TALER 3 (C) 2020-2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU Affero General Public License as 7 published by the Free Software Foundation; either version 3, 8 or (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with TALER; see the file COPYING. If not, 17 see <http://www.gnu.org/licenses/> 18 */ 19 20 /** 21 * @file taler-merchant-httpd_private-post-account.c 22 * @brief implementing POST /private/accounts request handling 23 * @author Christian Grothoff 24 */ 25 #include "platform.h" 26 #include "taler-merchant-httpd_private-post-account.h" 27 #include "taler-merchant-httpd_helper.h" 28 #include "taler_merchant_bank_lib.h" 29 #include <taler/taler_dbevents.h> 30 #include <taler/taler_json_lib.h> 31 #include "taler-merchant-httpd_mfa.h" 32 #include <regex.h> 33 34 35 MHD_RESULT 36 TMH_private_post_account (const struct TMH_RequestHandler *rh, 37 struct MHD_Connection *connection, 38 struct TMH_HandlerContext *hc) 39 { 40 struct TMH_MerchantInstance *mi = hc->instance; 41 const char *credit_facade_url = NULL; 42 const json_t *credit_facade_credentials = NULL; 43 struct TALER_FullPayto uri; 44 struct GNUNET_JSON_Specification ispec[] = { 45 TALER_JSON_spec_full_payto_uri ("payto_uri", 46 &uri), 47 GNUNET_JSON_spec_mark_optional ( 48 TALER_JSON_spec_web_url ("credit_facade_url", 49 &credit_facade_url), 50 NULL), 51 GNUNET_JSON_spec_mark_optional ( 52 GNUNET_JSON_spec_object_const ("credit_facade_credentials", 53 &credit_facade_credentials), 54 NULL), 55 GNUNET_JSON_spec_end () 56 }; 57 struct TMH_WireMethod *wm; 58 59 { 60 enum GNUNET_GenericReturnValue res; 61 62 res = TALER_MHD_parse_json_data (connection, 63 hc->request_body, 64 ispec); 65 if (GNUNET_OK != res) 66 return (GNUNET_NO == res) 67 ? MHD_YES 68 : MHD_NO; 69 } 70 71 { 72 char *err; 73 74 if (NULL != 75 (err = TALER_payto_validate (uri))) 76 { 77 MHD_RESULT mret; 78 79 GNUNET_break_op (0); 80 mret = TALER_MHD_reply_with_error (connection, 81 MHD_HTTP_BAD_REQUEST, 82 TALER_EC_GENERIC_PAYTO_URI_MALFORMED, 83 err); 84 GNUNET_free (err); 85 return mret; 86 } 87 } 88 { 89 char *apt = GNUNET_strdup (TMH_allowed_payment_targets); 90 char *method = TALER_payto_get_method (uri.full_payto); 91 bool ok; 92 93 ok = false; 94 for (const char *tok = strtok (apt, 95 " "); 96 NULL != tok; 97 tok = strtok (NULL, 98 " ")) 99 { 100 if (0 == strcmp ("*", 101 tok)) 102 ok = true; 103 if (0 == strcmp (method, 104 tok)) 105 ok = true; 106 if (ok) 107 break; 108 } 109 GNUNET_free (method); 110 GNUNET_free (apt); 111 if (! ok) 112 { 113 GNUNET_break_op (0); 114 return TALER_MHD_reply_with_error (connection, 115 MHD_HTTP_BAD_REQUEST, 116 TALER_EC_GENERIC_PAYTO_URI_MALFORMED, 117 "The payment target type is forbidden by policy"); 118 } 119 } 120 121 if ( (NULL != TMH_payment_target_regex) && 122 (0 != 123 regexec (&TMH_payment_target_re, 124 uri.full_payto, 125 0, 126 NULL, 127 0)) ) 128 { 129 GNUNET_break_op (0); 130 return TALER_MHD_reply_with_error (connection, 131 MHD_HTTP_BAD_REQUEST, 132 TALER_EC_GENERIC_PAYTO_URI_MALFORMED, 133 "The specific account is forbidden by policy"); 134 } 135 136 if ( (NULL == credit_facade_url) != 137 (NULL == credit_facade_credentials) ) 138 { 139 GNUNET_break_op (0); 140 return TALER_MHD_reply_with_error (connection, 141 MHD_HTTP_BAD_REQUEST, 142 TALER_EC_GENERIC_PARAMETER_MISSING, 143 (NULL == credit_facade_url) 144 ? "credit_facade_url" 145 : "credit_facade_credentials"); 146 } 147 if ( (NULL != credit_facade_url) || 148 (NULL != credit_facade_credentials) ) 149 { 150 struct TALER_MERCHANT_BANK_AuthenticationData auth; 151 152 if (GNUNET_OK != 153 TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials, 154 credit_facade_url, 155 &auth)) 156 { 157 GNUNET_break_op (0); 158 return TALER_MHD_reply_with_error (connection, 159 MHD_HTTP_BAD_REQUEST, 160 TALER_EC_GENERIC_PARAMETER_MALFORMED, 161 "credit_facade_url or credit_facade_credentials"); 162 } 163 TALER_MERCHANT_BANK_auth_free (&auth); 164 } 165 166 /* FIXME: might want to do all of the DB interactions below in one transaction... */ 167 { 168 enum GNUNET_DB_QueryStatus qs; 169 enum GNUNET_GenericReturnValue ret; 170 171 qs = TMH_db->select_accounts (TMH_db->cls, 172 mi->settings.id, 173 NULL, 174 NULL); 175 switch (qs) 176 { 177 case GNUNET_DB_STATUS_SOFT_ERROR: 178 case GNUNET_DB_STATUS_HARD_ERROR: 179 GNUNET_break (0); 180 return TALER_MHD_reply_with_error (connection, 181 MHD_HTTP_INTERNAL_SERVER_ERROR, 182 TALER_EC_GENERIC_DB_FETCH_FAILED, 183 "select_accounts"); 184 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 185 /* skip MFA */ 186 break; 187 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 188 ret = TMH_mfa_check_simple (hc, 189 TALER_MERCHANT_MFA_CO_ACCOUNT_CONFIGURATION, 190 mi); 191 192 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 193 "Account creation MFA check returned %d\n", 194 (int) ret); 195 if (GNUNET_OK != ret) 196 { 197 return (GNUNET_NO == ret) 198 ? MHD_YES 199 : MHD_NO; 200 } 201 } 202 } 203 204 /* convert provided payto URI into internal data structure with salts */ 205 wm = TMH_setup_wire_account (uri, 206 credit_facade_url, 207 credit_facade_credentials); 208 GNUNET_assert (NULL != wm); 209 { 210 struct TALER_MERCHANTDB_AccountDetails ad = { 211 .payto_uri = wm->payto_uri, 212 .salt = wm->wire_salt, 213 .instance_id = mi->settings.id, 214 .h_wire = wm->h_wire, 215 .credit_facade_url = wm->credit_facade_url, 216 .credit_facade_credentials = wm->credit_facade_credentials, 217 .active = wm->active 218 }; 219 enum GNUNET_DB_QueryStatus qs; 220 221 qs = TMH_db->insert_account (TMH_db->cls, 222 &ad); 223 switch (qs) 224 { 225 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 226 break; 227 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 228 /* conflict: account exists */ 229 { 230 struct TALER_MERCHANTDB_AccountDetails adx; 231 232 qs = TMH_db->select_account_by_uri (TMH_db->cls, 233 mi->settings.id, 234 ad.payto_uri, 235 &adx); 236 switch (qs) 237 { 238 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 239 if ( (0 == TALER_full_payto_cmp (adx.payto_uri, 240 ad.payto_uri) ) && 241 ( (adx.credit_facade_credentials == 242 ad.credit_facade_credentials) || 243 ( (NULL != adx.credit_facade_credentials) && 244 (NULL != ad.credit_facade_credentials) && 245 (1 == json_equal (adx.credit_facade_credentials, 246 ad.credit_facade_credentials)) ) ) && 247 ( (adx.credit_facade_url == ad.credit_facade_url) || 248 ( (NULL != adx.credit_facade_url) && 249 (NULL != ad.credit_facade_url) && 250 (0 == strcmp (adx.credit_facade_url, 251 ad.credit_facade_url)) ) ) ) 252 { 253 TMH_wire_method_free (wm); 254 GNUNET_free (adx.payto_uri.full_payto); 255 GNUNET_free (adx.credit_facade_url); 256 json_decref (adx.credit_facade_credentials); 257 return TALER_MHD_REPLY_JSON_PACK ( 258 connection, 259 MHD_HTTP_OK, 260 GNUNET_JSON_pack_data_auto ( 261 "salt", 262 &adx.salt), 263 GNUNET_JSON_pack_data_auto ( 264 "h_wire", 265 &adx.h_wire)); 266 } 267 GNUNET_free (adx.payto_uri.full_payto); 268 GNUNET_free (adx.credit_facade_url); 269 json_decref (adx.credit_facade_credentials); 270 break; 271 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 272 case GNUNET_DB_STATUS_SOFT_ERROR: 273 case GNUNET_DB_STATUS_HARD_ERROR: 274 GNUNET_break (0); 275 TMH_wire_method_free (wm); 276 return TALER_MHD_reply_with_error (connection, 277 MHD_HTTP_INTERNAL_SERVER_ERROR, 278 TALER_EC_GENERIC_DB_FETCH_FAILED, 279 "select_account"); 280 } 281 } 282 TMH_wire_method_free (wm); 283 return TALER_MHD_reply_with_error (connection, 284 MHD_HTTP_CONFLICT, 285 TALER_EC_MERCHANT_PRIVATE_ACCOUNT_EXISTS, 286 uri.full_payto); 287 case GNUNET_DB_STATUS_SOFT_ERROR: 288 case GNUNET_DB_STATUS_HARD_ERROR: 289 GNUNET_break (0); 290 TMH_wire_method_free (wm); 291 return TALER_MHD_reply_with_error (connection, 292 MHD_HTTP_INTERNAL_SERVER_ERROR, 293 TALER_EC_GENERIC_DB_STORE_FAILED, 294 "insert_account"); 295 } 296 } 297 298 { 299 struct GNUNET_DB_EventHeaderP es = { 300 .size = htons (sizeof (es)), 301 .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED) 302 }; 303 304 TMH_db->event_notify (TMH_db->cls, 305 &es, 306 NULL, 307 0); 308 } 309 /* Finally, also update our running process */ 310 GNUNET_CONTAINER_DLL_insert (mi->wm_head, 311 mi->wm_tail, 312 wm); 313 /* Note: we may not need to do this, as we notified 314 about the account change above. But also hardly hurts. */ 315 TMH_reload_instances (mi->settings.id); 316 return TALER_MHD_REPLY_JSON_PACK (connection, 317 MHD_HTTP_OK, 318 GNUNET_JSON_pack_data_auto ("salt", 319 &wm->wire_salt), 320 GNUNET_JSON_pack_data_auto ("h_wire", 321 &wm->h_wire)); 322 } 323 324 325 /* end of taler-merchant-httpd_private-post-account.c */