summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/authorization/anastasis-helper-authorization-iban.c343
-rw-r--r--src/include/anastasis_service.h14
-rw-r--r--src/restclient/anastasis_api_keyshare_lookup.c44
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;