diff options
author | Christian Grothoff <christian@grothoff.org> | 2021-08-15 11:53:51 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2021-08-15 11:53:51 +0200 |
commit | eabd8e45715a2d2c76f8eb8a99e09981b8a1b254 (patch) | |
tree | 02a8e573b15a5a4d7ecc806b12bdf524d50f5638 /src | |
parent | 7d9f1fbec3fcf1d38fe7d7cf89dfb7b8d8128775 (diff) | |
download | anastasis-eabd8e45715a2d2c76f8eb8a99e09981b8a1b254.tar.gz anastasis-eabd8e45715a2d2c76f8eb8a99e09981b8a1b254.tar.bz2 anastasis-eabd8e45715a2d2c76f8eb8a99e09981b8a1b254.zip |
-note left-over todos
Diffstat (limited to 'src')
-rw-r--r-- | src/authorization/anastasis-helper-authorization-iban.c | 343 | ||||
-rw-r--r-- | src/include/anastasis_service.h | 14 | ||||
-rw-r--r-- | src/restclient/anastasis_api_keyshare_lookup.c | 44 |
3 files changed, 381 insertions, 20 deletions
diff --git a/src/authorization/anastasis-helper-authorization-iban.c b/src/authorization/anastasis-helper-authorization-iban.c new file mode 100644 index 0000000..ce1d149 --- /dev/null +++ b/src/authorization/anastasis-helper-authorization-iban.c @@ -0,0 +1,343 @@ +/* + This file is part of Anastasis + Copyright (C) 2016--2021 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file anastasis-helper-authorization-iban.c + * @brief Process that watches for wire transfers to Anastasis bank account + * @author Christian Grothoff + * + * TODO: + * - blocked on #6992 + * - needs XXX_bank_service to access new facade once #6992 is implemented + * - needs to load authentication information + * - needs to load 'last known' transaction from DB + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include <jansson.h> +#include <pthread.h> +#include <microhttpd.h> +#include <taler/taler_json_lib.h> +#include <XXX_bank_service.h> +#include "anastasis_db_lib.h" + +/** + * How long to wait for an HTTP reply if there + * are no transactions pending at the server? + */ +#define LONGPOLL_TIMEOUT GNUNET_TIME_UNIT_HOURS + +/** + * Authentication data needed to access the account. + */ +static struct BANK_AccountInfo *auth; + +/** + * Active request for history. + */ +static struct BANK_CreditHistoryHandle *hh; + +/** + * Handle to the context for interacting with the bank. + */ +static struct GNUNET_CURL_Context *ctx; + +/** + * What is the last row ID that we have already processed? + */ +static uint64_t latest_row_off; + +/** + * Scheduler context for running the @e ctx. + */ +static struct GNUNET_CURL_RescheduleContext *rc; + +/** + * The configuration (global) + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Our DB plugin. + */ +static struct ANASTASIS_DatabasePlugin *db_plugin; + +/** + * How long should we sleep when idle before trying to find more work? + * Useful in case bank does not support long polling. + */ +static struct GNUNET_TIME_Relative idle_sleep_interval; + +/** + * Value to return from main(). 0 on success, non-zero on + * on serious errors. + */ +static int global_ret; + +/** + * Current task waiting for execution, if any. + */ +static struct GNUNET_SCHEDULER_Task *task; + + +/** + * We're being aborted with CTRL-C (or SIGTERM). Shut down. + * + * @param cls closure + */ +static void +shutdown_task (void *cls) +{ + (void) cls; + if (NULL != hh) + { + BANK_credit_history_cancel (hh); + hh = NULL; + } + if (NULL != ctx) + { + GNUNET_CURL_fini (ctx); + ctx = NULL; + } + if (NULL != rc) + { + GNUNET_CURL_gnunet_rc_destroy (rc); + rc = NULL; + } + if (NULL != task) + { + GNUNET_SCHEDULER_cancel (task); + task = NULL; + } + ANASTASIS_DB_plugin_unload (db_plugin); + db_plugin = NULL; + cfg = NULL; +} + + +/** + * Query for incoming wire transfers. + * + * @param cls NULL + */ +static void +find_transfers (void *cls); + + +/** + * Callbacks of this type are used to serve the result of asking + * the bank for the transaction history. + * + * @param cls closure with the `struct WioreAccount *` we are processing + * @param http_status HTTP status code from the server + * @param ec taler error code + * @param serial_id identification of the position at which we are querying + * @param details details about the wire transfer + * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration + */ +static int +history_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + uint64_t serial_id, + const struct BANK_CreditDetails *details) +{ + enum GNUNET_DB_QueryStatus qs; + + (void) json; + if (NULL == details) + { + hh = NULL; + if (TALER_EC_NONE != ec) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error fetching history: ec=%u, http_status=%u\n", + (unsigned int) ec, + http_status); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "End of list.\n"); + GNUNET_assert (NULL == task); + task = GNUNET_SCHEDULER_add_at (delayed_until, + &find_transfers, + NULL); + return GNUNET_OK; /* will be ignored anyway */ + } + if (serial_id <= latest_row_off) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Serial ID %llu not monotonic (got %llu before). Failing!\n", + (unsigned long long) serial_id, + (unsigned long long) latest_row_off); + GNUNET_SCHEDULER_shutdown (); + hh = NULL; + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding wire transfer over %s with (hashed) subject `%s'\n", + TALER_amount2s (&details->amount), + details->wire_subject); + qs = db_plugin->record_auth_iban_payment (db_plugin->cls, + serial_id, + details->wire_subject, + &details->amount, + details->debit_account_uri, + details->credit_account_uri, + details->execution_time); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + hh = NULL; + return GNUNET_SYSERR; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + hh = NULL; + return GNUNET_SYSERR; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* already existed (!?), should be impossible */ + GNUNET_break (0); + hh = NULL; + return GNUNET_SYSERR; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + /* normal case */ + break; + } + latest_row_off = serial_id; + return GNUNET_OK; +} + + +/** + * Query for incoming wire transfers. + * + * @param cls NULL + */ +static void +find_transfers (void *cls) +{ + enum GNUNET_DB_QueryStatus qs; + + (void) cls; + task = NULL; + GNUNET_assert (NULL == hh); + hh = BANK_credit_history (ctx, + &auth, + latest_row_off, + 1024, + test_mode + ? GNUNET_TIME_UNIT_ZERO + : LONGPOLL_TIMEOUT, + &history_cb, + NULL); + if (NULL == hh) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to start request for account history!\n"); + global_ret = EXIT_FAILURE; + GNUNET_SCHEDULER_shutdown (); + return; + } +} + + +/** + * First task. + * + * @param cls closure, NULL + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param c configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + (void) cls; + (void) args; + (void) cfgfile; + cfg = c; + if (NULL == + (db_plugin = ANASTASIS_DB_plugin_load (cfg))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to initialize DB subsystem\n"); + global_ret = EXIT_NOTCONFIGURED; + return; + } + // FIXME: initialize 'auth' from cfg! + + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + cls); + ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &rc); + rc = GNUNET_CURL_gnunet_rc_create (ctx); + if (NULL == ctx) + { + GNUNET_break (0); + return; + } + latest_row_off = FIXME; // need new DB function! + + task = GNUNET_SCHEDULER_add_now (&find_transfers, + NULL); +} + + +/** + * The main function of anastasis-helper-authorization-iban + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, non-zero on error + */ +int +main (int argc, + char *const *argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_flag ('t', + "test", + "run in test mode and exit when idle", + &test_mode), + GNUNET_GETOPT_OPTION_END + }; + enum GNUNET_GenericReturnValue ret; + + if (GNUNET_OK != + GNUNET_STRINGS_get_utf8_args (argc, argv, + &argc, &argv)) + return EXIT_INVALIDARGUMENT; + ANASTASIS_OS_init (); + ret = GNUNET_PROGRAM_run ( + argc, argv, + "anastasis-helper-authorization-iban", + gettext_noop ( + "background process that watches for incoming wire transfers from customers"), + options, + &run, NULL); + GNUNET_free_nz ((void *) argv); + if (GNUNET_SYSERR == ret) + return EXIT_INVALIDARGUMENT; + if (GNUNET_NO == ret) + return EXIT_SUCCESS; + return global_ret; +} + + +/* end of anastasis-helper-authorization-iban.c */ diff --git a/src/include/anastasis_service.h b/src/include/anastasis_service.h index a08f25b..bec89d1 100644 --- a/src/include/anastasis_service.h +++ b/src/include/anastasis_service.h @@ -475,7 +475,13 @@ enum ANASTASIS_KeyShareDownloadStatus * authentication check until the request timeout * was reached. The client should try again later. */ - ANASTASIS_KSD_AUTHENTICATION_TIMEOUT + ANASTASIS_KSD_AUTHENTICATION_TIMEOUT, + + /** + * The plugin provided external challenge instructions + * that should be followed. They are method-specific. + */ + ANASTASIS_KSD_EXTERNAL_CHALLENGE_INSTRUCTIONS }; @@ -584,6 +590,12 @@ struct ANASTASIS_KeyShareDownloadDetails } server_failure; + /** + * External challenge instructions, if @e status is + * #ANASTASIS_KSD_EXTERNAL_CHALLENGE_INSTRUCTIONS. + */ + const json_t *external_challenge; + } details; }; diff --git a/src/restclient/anastasis_api_keyshare_lookup.c b/src/restclient/anastasis_api_keyshare_lookup.c index 6df00dd..bb463b6 100644 --- a/src/restclient/anastasis_api_keyshare_lookup.c +++ b/src/restclient/anastasis_api_keyshare_lookup.c @@ -150,16 +150,30 @@ handle_keyshare_lookup_finished (void *cls, { GNUNET_break_op (0); kdd.status = ANASTASIS_KSD_SERVER_ERROR; + kdd.details.server_failure.http_status = MHD_HTTP_ACCEPTED; + kdd.details.server_failure.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } /* Success, call callback with all details! */ memcpy (&kdd.details.eks, data, data_size); - kslo->cb (kslo->cb_cls, - &kdd); - ANASTASIS_keyshare_lookup_cancel (kslo); - return; + break; + case MHD_HTTP_ACCEPTED: + kdd.details.external_challenge = json_loadb (data, + data_size, + JSON_REJECT_DUPLICATES, + NULL); + if (NULL == kdd.details.external_challenge) + { + GNUNET_break_op (0); + kdd.status = ANASTASIS_KSD_SERVER_ERROR; + kdd.details.server_failure.http_status = MHD_HTTP_ACCEPTED; + kdd.details.server_failure.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + kdd.status = ANASTASIS_KSD_EXTERNAL_CHALLENGE_INSTRUCTIONS; + break; case MHD_HTTP_BAD_REQUEST: /* This should never happen, either us or the anastasis server is buggy (or API version conflict); just pass JSON reply to the application */ @@ -206,24 +220,16 @@ handle_keyshare_lookup_finished (void *cls, /* Nothing really to verify, authentication required/failed */ kdd.status = ANASTASIS_KSD_REDIRECT_FOR_AUTHENTICATION; kdd.details.redirect_url = kslo->location; - kslo->cb (kslo->cb_cls, - &kdd); - ANASTASIS_keyshare_lookup_cancel (kslo); - return; + break; case MHD_HTTP_ALREADY_REPORTED: case MHD_HTTP_FORBIDDEN: /* Nothing really to verify, authentication required/failed */ - { - kdd.status = ANASTASIS_KSD_INVALID_ANSWER; - kdd.details.open_challenge.body = data; - kdd.details.open_challenge.body_size = data_size; - kdd.details.open_challenge.content_type = kslo->content_type; - kdd.details.open_challenge.http_status = response_code; - kslo->cb (kslo->cb_cls, - &kdd); - } - ANASTASIS_keyshare_lookup_cancel (kslo); - return; + kdd.status = ANASTASIS_KSD_INVALID_ANSWER; + kdd.details.open_challenge.body = data; + kdd.details.open_challenge.body_size = data_size; + kdd.details.open_challenge.content_type = kslo->content_type; + kdd.details.open_challenge.http_status = response_code; + break; case MHD_HTTP_NOT_FOUND: /* Nothing really to verify */ kdd.status = ANASTASIS_KSD_TRUTH_UNKNOWN; |