Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2025 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU General Public License as
7 : published by the Free Software Foundation; either version 3, or
8 : (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, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file src/testing/testing_api_cmd_instance_token.c
21 : * @brief command to test /private/token POSTing
22 : * @author Martin Schanzenbach
23 : */
24 : #include "platform.h"
25 : struct TokenInstanceState;
26 : #define TALER_MERCHANT_DELETE_PRIVATE_TOKEN_RESULT_CLOSURE struct TokenInstanceState
27 : #define TALER_MERCHANT_POST_PRIVATE_TOKEN_RESULT_CLOSURE struct TokenInstanceState
28 : #include <taler/taler_exchange_service.h>
29 : #include <taler/taler_testing_lib.h>
30 : #include "taler/taler_merchant_service.h"
31 : #include "taler/taler_merchant_testing_lib.h"
32 : #include <taler/merchant/delete-private-tokens-SERIAL.h>
33 : #include <taler/merchant/post-private-token.h>
34 :
35 :
36 : /**
37 : * State of a "POST /instances/$ID/private/token" CMD.
38 : */
39 : struct TokenInstanceState
40 : {
41 :
42 : /**
43 : * Handle for a "POST token" request.
44 : */
45 : struct TALER_MERCHANT_PostPrivateTokenHandle *itph;
46 :
47 : /**
48 : * Handle for a "DELETE token" request.
49 : */
50 : struct TALER_MERCHANT_DeletePrivateTokenHandle *itdh;
51 :
52 : /**
53 : * The interpreter state.
54 : */
55 : struct TALER_TESTING_Interpreter *is;
56 :
57 : /**
58 : * Base URL of the merchant serving the request.
59 : */
60 : const char *merchant_url;
61 :
62 : /**
63 : * ID of the instance to run GET for.
64 : */
65 : const char *instance_id;
66 :
67 : /**
68 : * The received token (if any).
69 : */
70 : char *token;
71 :
72 : /**
73 : * Desired scope. Can be NULL
74 : */
75 : const char *scope;
76 :
77 : /**
78 : * Desired duration.
79 : */
80 : struct GNUNET_TIME_Relative duration;
81 :
82 : /**
83 : * Refreshable?
84 : */
85 : bool refreshable;
86 :
87 : /**
88 : * Expected HTTP response code.
89 : */
90 : unsigned int http_status;
91 :
92 : /**
93 : * DELETE or POST.
94 : */
95 : unsigned int is_delete;
96 :
97 : };
98 :
99 : /**
100 : * Callback for a POST /instances/$ID/private/token operation.
101 : *
102 : * @param cls closure for this function
103 : * @param ptr response being processed
104 : */
105 : static void
106 4 : token_instance_post_cb (struct TokenInstanceState *tis,
107 : const struct TALER_MERCHANT_PostPrivateTokenResponse *
108 : ptr)
109 : {
110 :
111 4 : tis->itph = NULL;
112 4 : if (tis->http_status != ptr->hr.http_status)
113 : {
114 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
115 : "Unexpected response code %u (%d) to command %s\n",
116 : ptr->hr.http_status,
117 : (int) ptr->hr.ec,
118 : TALER_TESTING_interpreter_get_current_label (tis->is));
119 0 : TALER_TESTING_interpreter_fail (tis->is);
120 0 : return;
121 : }
122 4 : switch (ptr->hr.http_status)
123 : {
124 4 : case MHD_HTTP_OK:
125 4 : if (NULL != ptr->details.ok.access_token)
126 4 : tis->token = GNUNET_strdup (ptr->details.ok.access_token);
127 4 : break;
128 0 : case MHD_HTTP_BAD_REQUEST:
129 : /* likely invalid auth value, we do not check client-side */
130 0 : break;
131 0 : case MHD_HTTP_FORBIDDEN:
132 0 : break;
133 0 : default:
134 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
135 : "Unhandled HTTP status %u (%d) returned from /private/token operation.\n",
136 : ptr->hr.http_status,
137 : ptr->hr.ec);
138 : }
139 4 : TALER_TESTING_interpreter_next (tis->is);
140 : }
141 :
142 :
143 : /**
144 : * Callback for a DELETE /private/tokens/$SERIAL operation.
145 : *
146 : * @param cls closure for this function
147 : * @param dtr response being processed
148 : */
149 : static void
150 2 : token_instance_delete_cb (
151 : struct TokenInstanceState *tis,
152 : const struct TALER_MERCHANT_DeletePrivateTokenResponse *dtr)
153 : {
154 :
155 2 : tis->itdh = NULL;
156 2 : if (tis->http_status != dtr->hr.http_status)
157 : {
158 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
159 : "Unexpected response code %u (%d) to command %s\n",
160 : dtr->hr.http_status,
161 : (int) dtr->hr.ec,
162 : TALER_TESTING_interpreter_get_current_label (tis->is));
163 0 : TALER_TESTING_interpreter_fail (tis->is);
164 0 : return;
165 : }
166 2 : switch (dtr->hr.http_status)
167 : {
168 2 : case MHD_HTTP_NO_CONTENT:
169 2 : break;
170 0 : case MHD_HTTP_FORBIDDEN:
171 0 : break;
172 0 : default:
173 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
174 : "Unhandled HTTP status %u (%d) returned from DELETE /private/tokens operation.\n",
175 : dtr->hr.http_status,
176 : dtr->hr.ec);
177 : }
178 2 : TALER_TESTING_interpreter_next (tis->is);
179 : }
180 :
181 :
182 : /**
183 : * set a token
184 : *
185 : *
186 : * @param cls closure.
187 : * @param cmd command being run now.
188 : * @param is interpreter state.
189 : */
190 : static void
191 4 : set_token_instance_run (void *cls,
192 : const struct TALER_TESTING_Command *cmd,
193 : struct TALER_TESTING_Interpreter *is)
194 : {
195 4 : const char *token_job_label = cls;
196 : const char *token;
197 : const struct TALER_TESTING_Command *tok_cmd;
198 : struct GNUNET_CURL_Context *cctx;
199 : char *authorization;
200 :
201 4 : cctx = TALER_TESTING_interpreter_get_context (is);
202 4 : GNUNET_assert (NULL != cctx);
203 4 : tok_cmd = TALER_TESTING_interpreter_lookup_command (
204 : is,
205 : token_job_label);
206 4 : if (NULL == tok_cmd)
207 : {
208 0 : GNUNET_break (0);
209 0 : TALER_TESTING_interpreter_fail (is);
210 0 : return;
211 : }
212 4 : if (GNUNET_OK !=
213 4 : TALER_TESTING_get_trait_bearer_token (tok_cmd,
214 : &token))
215 : {
216 0 : GNUNET_break (0);
217 0 : TALER_TESTING_interpreter_fail (is);
218 0 : return;
219 : }
220 4 : GNUNET_assert (NULL != token);
221 :
222 4 : GNUNET_asprintf (&authorization,
223 : "%s: Bearer %s",
224 : MHD_HTTP_HEADER_AUTHORIZATION,
225 : token);
226 4 : GNUNET_assert (GNUNET_OK ==
227 : GNUNET_CURL_append_header (cctx,
228 : authorization));
229 4 : GNUNET_free (authorization);
230 4 : TALER_TESTING_interpreter_next (is);
231 : }
232 :
233 :
234 : /**
235 : * Run the "token /instances/$ID" CMD.
236 : *
237 : * @param cls closure.
238 : * @param cmd command being run now.
239 : * @param is interpreter state.
240 : */
241 : static void
242 6 : token_instance_run (void *cls,
243 : const struct TALER_TESTING_Command *cmd,
244 : struct TALER_TESTING_Interpreter *is)
245 : {
246 6 : struct TokenInstanceState *tis = cls;
247 :
248 6 : tis->is = is;
249 6 : if (GNUNET_NO == tis->is_delete)
250 : {
251 4 : tis->itph = TALER_MERCHANT_post_private_token_create (
252 : TALER_TESTING_interpreter_get_context (is),
253 : tis->merchant_url,
254 : tis->instance_id,
255 : tis->scope);
256 4 : GNUNET_assert (GNUNET_OK ==
257 : TALER_MERCHANT_post_private_token_set_options (
258 : tis->itph,
259 : TALER_MERCHANT_post_private_token_option_duration (
260 : tis->duration),
261 : TALER_MERCHANT_post_private_token_option_refreshable (
262 : tis->refreshable)));
263 : {
264 : enum TALER_ErrorCode ec;
265 :
266 4 : ec = TALER_MERCHANT_post_private_token_start (
267 : tis->itph,
268 : &token_instance_post_cb,
269 : tis);
270 4 : GNUNET_assert (TALER_EC_NONE == ec);
271 : }
272 : }
273 : else
274 : {
275 2 : tis->itdh = TALER_MERCHANT_delete_private_token_create (
276 : TALER_TESTING_interpreter_get_context (is),
277 : tis->merchant_url,
278 : tis->instance_id);
279 : {
280 : enum TALER_ErrorCode ec;
281 :
282 2 : ec = TALER_MERCHANT_delete_private_token_start (
283 : tis->itdh,
284 : &token_instance_delete_cb,
285 : tis);
286 2 : GNUNET_assert (TALER_EC_NONE == ec);
287 : }
288 : }
289 6 : }
290 :
291 :
292 : /**
293 : * Free the state of a "POST instance token" CMD, and possibly
294 : * cancel a pending operation thereof.
295 : *
296 : * @param cls closure.
297 : * @param cmd command being run.
298 : */
299 : static void
300 6 : token_instance_cleanup (void *cls,
301 : const struct TALER_TESTING_Command *cmd)
302 : {
303 6 : struct TokenInstanceState *tis = cls;
304 :
305 6 : if (NULL != tis->itph)
306 : {
307 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
308 : "POST /instance/$ID/token operation did not complete\n");
309 0 : TALER_MERCHANT_post_private_token_cancel (tis->itph);
310 : }
311 6 : if (NULL != tis->itdh)
312 : {
313 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
314 : "DELETE /instance/$ID/token operation did not complete\n");
315 0 : TALER_MERCHANT_delete_private_token_cancel (tis->itdh);
316 : }
317 6 : GNUNET_free (tis->token);
318 6 : GNUNET_free (tis);
319 6 : }
320 :
321 :
322 : /**
323 : * Offer internal data to other commands.
324 : *
325 : * @param cls closure
326 : * @param[out] ret result (could be anything)
327 : * @param trait name of the trait
328 : * @param index index number of the object to extract.
329 : * @return #GNUNET_OK on success
330 : */
331 : static enum GNUNET_GenericReturnValue
332 4 : token_instance_traits (void *cls,
333 : const void **ret,
334 : const char *trait,
335 : unsigned int index)
336 : {
337 4 : struct TokenInstanceState *ais = cls;
338 : struct TALER_TESTING_Trait traits[] = {
339 4 : TALER_TESTING_make_trait_bearer_token (ais->token),
340 4 : TALER_TESTING_trait_end ()
341 : };
342 :
343 4 : return TALER_TESTING_get_trait (traits,
344 : ret,
345 : trait,
346 : index);
347 : }
348 :
349 :
350 : struct TALER_TESTING_Command
351 2 : TALER_TESTING_cmd_merchant_delete_instance_token (const char *label,
352 : const char *merchant_url,
353 : const char *instance_id,
354 : unsigned int http_status)
355 : {
356 : struct TokenInstanceState *tis;
357 :
358 2 : tis = GNUNET_new (struct TokenInstanceState);
359 2 : tis->merchant_url = merchant_url;
360 2 : tis->instance_id = instance_id;
361 2 : tis->is_delete = GNUNET_YES;
362 2 : tis->http_status = http_status;
363 :
364 : {
365 2 : struct TALER_TESTING_Command cmd = {
366 : .cls = tis,
367 : .label = label,
368 : .run = &token_instance_run,
369 : .cleanup = &token_instance_cleanup,
370 : .traits = &token_instance_traits
371 : };
372 :
373 2 : return cmd;
374 : }
375 : }
376 :
377 :
378 : struct TALER_TESTING_Command
379 4 : TALER_TESTING_cmd_merchant_set_instance_token (const char *label,
380 : const char *token_job_label)
381 : {
382 : {
383 4 : struct TALER_TESTING_Command cmd = {
384 : .cls = (void*) token_job_label, // FIXME scope
385 : .label = label,
386 : .run = &set_token_instance_run,
387 : .cleanup = NULL,
388 : .traits = NULL
389 : };
390 :
391 4 : return cmd;
392 : }
393 : }
394 :
395 :
396 : struct TALER_TESTING_Command
397 4 : TALER_TESTING_cmd_merchant_post_instance_token (const char *label,
398 : const char *merchant_url,
399 : const char *instance_id,
400 : const char *scope,
401 : struct GNUNET_TIME_Relative
402 : duration,
403 : bool refreshable,
404 : unsigned int http_status)
405 : {
406 : struct TokenInstanceState *tis;
407 :
408 4 : tis = GNUNET_new (struct TokenInstanceState);
409 4 : tis->merchant_url = merchant_url;
410 4 : tis->instance_id = instance_id;
411 4 : tis->scope = scope;
412 4 : tis->duration = duration;
413 4 : tis->refreshable = refreshable;
414 4 : tis->is_delete = GNUNET_NO;
415 4 : tis->http_status = http_status;
416 :
417 : {
418 4 : struct TALER_TESTING_Command cmd = {
419 : .cls = tis,
420 : .label = label,
421 : .run = &token_instance_run,
422 : .cleanup = &token_instance_cleanup,
423 : .traits = &token_instance_traits
424 : };
425 :
426 4 : return cmd;
427 : }
428 : }
429 :
430 :
431 : /* end of testing_api_cmd_token_instance.c */
|