Line data Source code
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 : * Restrictions imposed when crediting this account.
64 : */
65 : const json_t *credit_restrictions;
66 :
67 : /**
68 : * Restrictions imposed when debiting this account.
69 : */
70 : const json_t *debit_restrictions;
71 :
72 : /**
73 : * Timestamp for checking against replay attacks.
74 : */
75 : struct GNUNET_TIME_Timestamp validity_start;
76 :
77 : /**
78 : * Label to use for this bank. Default is empty.
79 : */
80 : const char *bank_label;
81 :
82 : /**
83 : * Priority of the bank in the list. Default 0.
84 : */
85 : int64_t priority;
86 :
87 : };
88 :
89 :
90 : /**
91 : * Function implementing database transaction to add an wire. Runs the
92 : * transaction logic; IF it returns a non-error code, the transaction logic
93 : * MUST NOT queue a MHD response. IF it returns an hard error, the
94 : * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
95 : * returns the soft error code, the function MAY be called again to retry and
96 : * MUST not queue a MHD response.
97 : *
98 : * @param cls closure with a `struct AddWireContext`
99 : * @param connection MHD request which triggered the transaction
100 : * @param[out] mhd_ret set to MHD response status for @a connection,
101 : * if transaction failed (!)
102 : * @return transaction status
103 : */
104 : static enum GNUNET_DB_QueryStatus
105 25 : add_wire (void *cls,
106 : struct MHD_Connection *connection,
107 : MHD_RESULT *mhd_ret)
108 : {
109 25 : struct AddWireContext *awc = cls;
110 : struct GNUNET_TIME_Timestamp last_date;
111 : enum GNUNET_DB_QueryStatus qs;
112 :
113 25 : qs = TEH_plugin->lookup_wire_timestamp (TEH_plugin->cls,
114 : awc->payto_uri,
115 : &last_date);
116 25 : if (qs < 0)
117 : {
118 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
119 0 : return qs;
120 0 : GNUNET_break (0);
121 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
122 : MHD_HTTP_INTERNAL_SERVER_ERROR,
123 : TALER_EC_GENERIC_DB_FETCH_FAILED,
124 : "lookup wire");
125 0 : return qs;
126 : }
127 25 : if ( (0 < qs) &&
128 2 : (GNUNET_TIME_timestamp_cmp (last_date,
129 : >,
130 : awc->validity_start)) )
131 : {
132 0 : *mhd_ret = TALER_MHD_reply_with_error (
133 : connection,
134 : MHD_HTTP_CONFLICT,
135 : TALER_EC_EXCHANGE_MANAGEMENT_WIRE_MORE_RECENT_PRESENT,
136 : NULL);
137 0 : return GNUNET_DB_STATUS_HARD_ERROR;
138 : }
139 25 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
140 23 : qs = TEH_plugin->insert_wire (TEH_plugin->cls,
141 : awc->payto_uri,
142 : awc->conversion_url,
143 : awc->debit_restrictions,
144 : awc->credit_restrictions,
145 : awc->validity_start,
146 23 : &awc->master_sig_wire,
147 : awc->bank_label,
148 : awc->priority);
149 : else
150 2 : qs = TEH_plugin->update_wire (TEH_plugin->cls,
151 : awc->payto_uri,
152 : awc->conversion_url,
153 : awc->debit_restrictions,
154 : awc->credit_restrictions,
155 : awc->validity_start,
156 2 : &awc->master_sig_wire,
157 : awc->bank_label,
158 : awc->priority,
159 : true);
160 25 : if (qs < 0)
161 : {
162 0 : GNUNET_break (0);
163 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
164 0 : return qs;
165 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
166 : MHD_HTTP_INTERNAL_SERVER_ERROR,
167 : TALER_EC_GENERIC_DB_STORE_FAILED,
168 : "add wire");
169 0 : return qs;
170 : }
171 25 : return qs;
172 : }
173 :
174 :
175 : MHD_RESULT
176 27 : TEH_handler_management_post_wire (
177 : struct MHD_Connection *connection,
178 : const json_t *root)
179 : {
180 27 : struct AddWireContext awc = {
181 : .conversion_url = NULL
182 : };
183 : struct GNUNET_JSON_Specification spec[] = {
184 27 : GNUNET_JSON_spec_fixed_auto ("master_sig_wire",
185 : &awc.master_sig_wire),
186 27 : GNUNET_JSON_spec_fixed_auto ("master_sig_add",
187 : &awc.master_sig_add),
188 27 : TALER_JSON_spec_full_payto_uri ("payto_uri",
189 : &awc.payto_uri),
190 27 : GNUNET_JSON_spec_mark_optional (
191 : TALER_JSON_spec_web_url ("conversion_url",
192 : &awc.conversion_url),
193 : NULL),
194 27 : GNUNET_JSON_spec_array_const ("credit_restrictions",
195 : &awc.credit_restrictions),
196 27 : GNUNET_JSON_spec_array_const ("debit_restrictions",
197 : &awc.debit_restrictions),
198 27 : GNUNET_JSON_spec_timestamp ("validity_start",
199 : &awc.validity_start),
200 27 : GNUNET_JSON_spec_mark_optional (
201 : GNUNET_JSON_spec_string ("bank_label",
202 : &awc.bank_label),
203 : NULL),
204 27 : GNUNET_JSON_spec_mark_optional (
205 : GNUNET_JSON_spec_int64 ("priority",
206 : &awc.priority),
207 : NULL),
208 27 : GNUNET_JSON_spec_end ()
209 : };
210 :
211 : {
212 : enum GNUNET_GenericReturnValue res;
213 :
214 27 : res = TALER_MHD_parse_json_data (connection,
215 : root,
216 : spec);
217 27 : if (GNUNET_SYSERR == res)
218 0 : return MHD_NO; /* hard failure */
219 27 : if (GNUNET_NO == res)
220 0 : return MHD_YES; /* failure */
221 : }
222 27 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
223 : {
224 27 : char *msg = TALER_payto_validate (awc.payto_uri);
225 :
226 27 : if (NULL != msg)
227 : {
228 : MHD_RESULT ret;
229 :
230 0 : GNUNET_break_op (0);
231 0 : ret = TALER_MHD_reply_with_error (
232 : connection,
233 : MHD_HTTP_BAD_REQUEST,
234 : TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
235 : msg);
236 0 : GNUNET_JSON_parse_free (spec);
237 0 : GNUNET_free (msg);
238 0 : return ret;
239 : }
240 : }
241 27 : if (GNUNET_OK !=
242 27 : TALER_exchange_offline_wire_add_verify (
243 : awc.payto_uri,
244 : awc.conversion_url,
245 : awc.debit_restrictions,
246 : awc.credit_restrictions,
247 : awc.validity_start,
248 : &TEH_master_public_key,
249 : &awc.master_sig_add))
250 : {
251 2 : GNUNET_break_op (0);
252 2 : GNUNET_JSON_parse_free (spec);
253 2 : return TALER_MHD_reply_with_error (
254 : connection,
255 : MHD_HTTP_FORBIDDEN,
256 : TALER_EC_EXCHANGE_MANAGEMENT_WIRE_ADD_SIGNATURE_INVALID,
257 : NULL);
258 : }
259 25 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
260 25 : if (GNUNET_OK !=
261 25 : TALER_exchange_wire_signature_check (
262 : awc.payto_uri,
263 : awc.conversion_url,
264 : awc.debit_restrictions,
265 : awc.credit_restrictions,
266 : &TEH_master_public_key,
267 : &awc.master_sig_wire))
268 : {
269 0 : GNUNET_break_op (0);
270 0 : GNUNET_JSON_parse_free (spec);
271 0 : return TALER_MHD_reply_with_error (
272 : connection,
273 : MHD_HTTP_FORBIDDEN,
274 : TALER_EC_EXCHANGE_MANAGEMENT_WIRE_DETAILS_SIGNATURE_INVALID,
275 : NULL);
276 : }
277 : {
278 : char *wire_method;
279 :
280 25 : wire_method = TALER_payto_get_method (awc.payto_uri.full_payto);
281 25 : if (NULL == wire_method)
282 : {
283 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
284 : "payto:// URI `%s' is malformed\n",
285 : awc.payto_uri.full_payto);
286 0 : GNUNET_JSON_parse_free (spec);
287 0 : return TALER_MHD_reply_with_error (
288 : connection,
289 : MHD_HTTP_BAD_REQUEST,
290 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
291 : "payto_uri");
292 : }
293 25 : GNUNET_free (wire_method);
294 : }
295 :
296 : {
297 : enum GNUNET_GenericReturnValue res;
298 : MHD_RESULT ret;
299 :
300 25 : res = TEH_DB_run_transaction (connection,
301 : "add wire",
302 : TEH_MT_REQUEST_OTHER,
303 : &ret,
304 : &add_wire,
305 : &awc);
306 25 : GNUNET_JSON_parse_free (spec);
307 25 : if (GNUNET_SYSERR == res)
308 0 : return ret;
309 : }
310 25 : TEH_wire_update_state ();
311 25 : return TALER_MHD_reply_static (
312 : connection,
313 : MHD_HTTP_NO_CONTENT,
314 : NULL,
315 : NULL,
316 : 0);
317 : }
318 :
319 :
320 : /* end of taler-exchange-httpd_management_wire.c */
|