summaryrefslogtreecommitdiff
path: root/src/cli
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli')
-rw-r--r--src/cli/.gitignore6
-rw-r--r--src/cli/Makefile.am56
-rw-r--r--src/cli/anastasis-cli-redux.c366
-rw-r--r--src/cli/resources/00-backup.json8
-rw-r--r--src/cli/resources/00-recovery.json8
-rw-r--r--src/cli/resources/01-backup.json41
-rw-r--r--src/cli/resources/01-recovery.json41
-rw-r--r--src/cli/resources/02-backup.json83
-rw-r--r--src/cli/resources/02-recovery.json83
-rw-r--r--src/cli/resources/03-backup.json155
-rw-r--r--src/cli/resources/04-backup.json172
-rw-r--r--src/cli/resources/05-backup.json213
-rw-r--r--src/cli/resources/06-backup.json223
-rw-r--r--src/cli/test_anastasis_reducer_1.conf9
-rw-r--r--src/cli/test_anastasis_reducer_2.conf9
-rw-r--r--src/cli/test_anastasis_reducer_3.conf9
-rw-r--r--src/cli/test_anastasis_reducer_4.conf9
-rwxr-xr-xsrc/cli/test_anastasis_reducer_add_authentication.sh134
-rwxr-xr-xsrc/cli/test_anastasis_reducer_backup_enter_user_attributes.sh140
-rwxr-xr-xsrc/cli/test_anastasis_reducer_done_authentication.sh65
-rwxr-xr-xsrc/cli/test_anastasis_reducer_done_policy_review.sh105
-rwxr-xr-xsrc/cli/test_anastasis_reducer_enter_secret.sh417
-rwxr-xr-xsrc/cli/test_anastasis_reducer_initialize_state.sh64
-rwxr-xr-xsrc/cli/test_anastasis_reducer_recovery_enter_user_attributes.sh516
-rwxr-xr-xsrc/cli/test_anastasis_reducer_select_continent.sh116
-rwxr-xr-xsrc/cli/test_anastasis_reducer_select_country.sh144
-rw-r--r--src/cli/test_reducer.conf197
-rw-r--r--src/cli/user-details-example.json6
28 files changed, 3395 insertions, 0 deletions
diff --git a/src/cli/.gitignore b/src/cli/.gitignore
new file mode 100644
index 0000000..dbf01fa
--- /dev/null
+++ b/src/cli/.gitignore
@@ -0,0 +1,6 @@
+*.log
+anastasis-reducer
+test_reducer_home
+*.trs
+taler-bank.err
+wallet.err
diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am
new file mode 100644
index 0000000..6b8bf23
--- /dev/null
+++ b/src/cli/Makefile.am
@@ -0,0 +1,56 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+bin_PROGRAMS = \
+ anastasis-reducer
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+ XLIB = -lgcov
+endif
+
+check_SCRIPTS = \
+ test_anastasis_reducer_initialize_state.sh \
+ test_anastasis_reducer_select_continent.sh \
+ test_anastasis_reducer_select_country.sh \
+ test_anastasis_reducer_backup_enter_user_attributes.sh \
+ test_anastasis_reducer_add_authentication.sh \
+ test_anastasis_reducer_done_authentication.sh \
+ test_anastasis_reducer_done_policy_review.sh \
+ test_anastasis_reducer_enter_secret.sh \
+ test_anastasis_reducer_recovery_enter_user_attributes.sh
+
+
+AM_TESTS_ENVIRONMENT=export ANASTASIS_PREFIX=$${ANASTASIS_PREFIX:-@libdir@};export PATH=$${ANASTASIS_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
+
+TESTS = \
+ $(check_SCRIPTS)
+
+EXTRA_DIST = \
+ $(check_SCRIPTS) \
+ test_reducer.conf \
+ test_anastasis_reducer_1.conf \
+ test_anastasis_reducer_2.conf \
+ test_anastasis_reducer_3.conf \
+ test_anastasis_reducer_4.conf \
+ resources/00-backup.json \
+ resources/01-backup.json \
+ resources/02-backup.json \
+ resources/03-backup.json \
+ resources/04-backup.json \
+ resources/00-recovery.json \
+ resources/01-recovery.json \
+ resources/02-recovery.json
+
+anastasis_reducer_SOURCES = \
+ anastasis-cli-redux.c
+anastasis_reducer_LDADD = \
+ $(top_builddir)/src/util/libanastasisutil.la \
+ $(top_builddir)/src/reducer/libanastasisredux.la \
+ -ltalerjson \
+ -ltalerutil \
+ -lgnunetjson \
+ -lgnunetcurl \
+ -lgnunetutil \
+ -ljansson \
+ $(XLIB)
diff --git a/src/cli/anastasis-cli-redux.c b/src/cli/anastasis-cli-redux.c
new file mode 100644
index 0000000..7b533c2
--- /dev/null
+++ b/src/cli/anastasis-cli-redux.c
@@ -0,0 +1,366 @@
+/*
+ This file is part of Anastasis
+ Copyright (C) 2020,2021 Taler Systems SA
+
+ Anastasis is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file cli/anastasis-cli-redux.c
+ * @brief command line tool for our reducer
+ * @author Christian Grothoff
+ * @author Dennis Neufeld
+ * @author Dominik Meister
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "anastasis_redux.h"
+#include <taler/taler_util.h>
+#include <taler/taler_error_codes.h>
+#include <taler/taler_json_lib.h>
+#include "anastasis_util_lib.h"
+
+/**
+ * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
+ */
+static struct GNUNET_CURL_RescheduleContext *rc;
+
+/**
+ * Curl context for communication with anastasis backend
+ */
+static struct GNUNET_CURL_Context *ctx;
+
+/**
+ * -b option given.
+ */
+static int b_flag;
+
+/**
+ * -r option given.
+ */
+static int r_flag;
+
+/**
+ * Input to -a option given.
+ */
+static char *input;
+
+/**
+ * Output filename, if given.
+ */
+static char *output_filename;
+
+/**
+ * JSON containing previous state
+ */
+static json_t *prev_state;
+
+/**
+ * JSON containing arguments for action
+ */
+static json_t *arguments;
+
+/**
+ * Handle to an ongoing action.
+ */
+static struct ANASTASIS_ReduxAction *ra;
+
+/**
+ * Return value from main.
+ */
+static int global_ret;
+
+
+/**
+ * Persist a json state, report errors.
+ *
+ * @param state to persist
+ * @param filename where to write the state to, NULL for stdout
+ */
+static void
+persist_new_state (json_t *state,
+ const char *filename)
+{
+ if (NULL != filename)
+ {
+ if (0 !=
+ json_dump_file (state,
+ filename,
+ JSON_COMPACT))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not dump state to `%s'\n",
+ filename);
+ return;
+ }
+ return;
+ }
+ {
+ char *state_str = json_dumps (state,
+ JSON_COMPACT);
+ if (-1 >=
+ fprintf (stdout,
+ "%s",
+ state_str))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not dump state to stdout\n");
+ GNUNET_free (state_str);
+ return;
+ }
+ GNUNET_free (state_str);
+ }
+}
+
+
+/**
+ * Function called with the results of #ANASTASIS_backup_action
+ * or #ANASTASIS_recovery_action.
+ *
+ * @param cls closure
+ * @param error_code Error code
+ * @param new_state new state as result
+ */
+static void
+action_cb (void *cls,
+ enum TALER_ErrorCode error_code,
+ json_t *result_state)
+{
+ (void) cls;
+ ra = NULL;
+ if (NULL != result_state)
+ persist_new_state (result_state,
+ output_filename);
+ if (TALER_EC_NONE != error_code)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Redux failed with error %d: %s\n",
+ error_code,
+ TALER_ErrorCode_get_hint (error_code));
+ }
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = (TALER_EC_NONE != error_code) ? 1 : 0;
+}
+
+
+/**
+ * @brief Shutdown the application.
+ *
+ * @param cls closure
+ */
+static void
+shutdown_task (void *cls)
+{
+ (void) cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Shutdown initiated\n");
+ if (NULL != ra)
+ {
+ ANASTASIS_redux_action_cancel (ra);
+ ra = NULL;
+ }
+ ANASTASIS_redux_done ();
+ if (NULL != ctx)
+ {
+ GNUNET_CURL_fini (ctx);
+ ctx = NULL;
+ }
+ if (NULL != rc)
+ {
+ GNUNET_CURL_gnunet_rc_destroy (rc);
+ rc = NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Shutdown complete\n");
+}
+
+
+/**
+ * @brief Start the application
+ *
+ * @param cls closure
+ * @param args arguments left
+ * @param cfgfile config file name
+ * @param cfg handle for the configuration file
+ */
+static void
+run (void *cls,
+ char *const *args,
+ const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ (void) cls;
+ json_error_t error;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Starting anastasis-reducer\n");
+ GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
+ NULL);
+ if (b_flag && r_flag)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "We cannot start backup and recovery at the same time!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (r_flag)
+ {
+ json_t *init_state;
+
+ init_state = ANASTASIS_recovery_start (cfg);
+ if (NULL == init_state)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Failed to create an initial recovery state!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ persist_new_state (init_state,
+ args[0]);
+ json_decref (init_state);
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (b_flag)
+ {
+ json_t *init_state;
+
+ init_state = ANASTASIS_backup_start (cfg);
+ if (NULL == init_state)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Failed to create an initial backup state!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ persist_new_state (init_state,
+ args[0]);
+ json_decref (init_state);
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+
+ /* action processing */
+ {
+ const char *action = args[0];
+
+ if (NULL == action)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "You must specify an action as the first argument (or `-b' or `-r')\n");
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Example: anastasis-reducer back\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ args++;
+ if (NULL != input)
+ {
+ arguments = json_loads (input,
+ JSON_DECODE_ANY,
+ &error);
+ if (NULL == arguments)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Failed to parse arguments on line %u:%u: %s!\n",
+ error.line,
+ error.column,
+ error.text);
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ }
+ if (NULL != args[0])
+ {
+ prev_state = json_load_file (args[0],
+ JSON_DECODE_ANY,
+ &error);
+ args++;
+ }
+ else
+ {
+ prev_state = json_loadf (stdin,
+ JSON_DECODE_ANY,
+ &error);
+ }
+ if (NULL == prev_state)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Failed to parse initial state on line %u:%u: %s!\n",
+ error.line,
+ error.column,
+ error.text);
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ output_filename = args[0];
+ /* initialize HTTP client event loop */
+ ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
+ &rc);
+ rc = GNUNET_CURL_gnunet_rc_create (ctx);
+ ANASTASIS_redux_init (ctx);
+ ra = ANASTASIS_redux_action (prev_state,
+ action,
+ arguments,
+ &action_cb,
+ cls);
+ }
+}
+
+
+int
+main (int argc,
+ char *const *argv)
+{
+ /* the available command line options */
+ struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_option_flag ('b',
+ "backup",
+ "use reducer to handle states for backup process",
+ &b_flag),
+ GNUNET_GETOPT_option_flag ('r',
+ "restore",
+ "use reducer to handle states for restore process",
+ &r_flag),
+ GNUNET_GETOPT_option_string ('a',
+ "arguments",
+ "JSON",
+ "pass a JSON string containing arguments to reducer",
+ &input),
+
+ GNUNET_GETOPT_OPTION_END
+ };
+ enum GNUNET_GenericReturnValue ret;
+
+ /* FIRST get the libtalerutil initialization out
+ of the way. Then throw that one away, and force
+ the SYNC defaults to be used! */
+ (void) TALER_project_data_default ();
+ GNUNET_OS_init (ANASTASIS_project_data_default ());
+ ret = GNUNET_PROGRAM_run (argc,
+ argv,
+ "anastasis-reducer",
+ "This is an application for using Anastasis to handle the states.\n",
+ options,
+ &run,
+ NULL);
+ if (GNUNET_SYSERR == ret)
+ return 3;
+ if (GNUNET_NO == ret)
+ return 0;
+ return global_ret;
+}
+
+
+/* end of anastasis-cli-redux.c */
diff --git a/src/cli/resources/00-backup.json b/src/cli/resources/00-backup.json
new file mode 100644
index 0000000..6e6c320
--- /dev/null
+++ b/src/cli/resources/00-backup.json
@@ -0,0 +1,8 @@
+{
+ "continents": [
+ "Europe",
+ "North America",
+ "Testcontinent"
+ ],
+ "backup_state": "CONTINENT_SELECTING"
+} \ No newline at end of file
diff --git a/src/cli/resources/00-recovery.json b/src/cli/resources/00-recovery.json
new file mode 100644
index 0000000..acff19a
--- /dev/null
+++ b/src/cli/resources/00-recovery.json
@@ -0,0 +1,8 @@
+{
+ "continents": [
+ "Europe",
+ "North America",
+ "Testcontinent"
+ ],
+ "recovery_state": "CONTINENT_SELECTING"
+} \ No newline at end of file
diff --git a/src/cli/resources/01-backup.json b/src/cli/resources/01-backup.json
new file mode 100644
index 0000000..842d3af
--- /dev/null
+++ b/src/cli/resources/01-backup.json
@@ -0,0 +1,41 @@
+{
+ "continents": [
+ "Europe",
+ "North America",
+ "Testcontinent"
+ ],
+ "backup_state": "COUNTRY_SELECTING",
+ "selected_continent": "Testcontinent",
+ "countries": [
+ {
+ "code": "xx",
+ "name": "Testland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Testlandt",
+ "de_CH": "Testlandi",
+ "fr": "Testpais",
+ "en": "Testland"
+ },
+ "currency": "TESTKUDOS"
+ },
+ {
+ "code": "xy",
+ "name": "Demoland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Demolandt",
+ "de_CH": "Demolandi",
+ "fr": "Demopais",
+ "en": "Demoland"
+ },
+ "currency": "KUDOS"
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/cli/resources/01-recovery.json b/src/cli/resources/01-recovery.json
new file mode 100644
index 0000000..11aafd3
--- /dev/null
+++ b/src/cli/resources/01-recovery.json
@@ -0,0 +1,41 @@
+{
+ "continents": [
+ "Europe",
+ "North America",
+ "Testcontinent"
+ ],
+ "recovery_state": "COUNTRY_SELECTING",
+ "selected_continent": "Testcontinent",
+ "countries": [
+ {
+ "code": "xx",
+ "name": "Testland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Testlandt",
+ "de_CH": "Testlandi",
+ "fr": "Testpais",
+ "en": "Testland"
+ },
+ "currency": "TESTKUDOS"
+ },
+ {
+ "code": "xy",
+ "name": "Demoland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Demolandt",
+ "de_CH": "Demolandi",
+ "fr": "Demopais",
+ "en": "Demoland"
+ },
+ "currency": "KUDOS"
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/cli/resources/02-backup.json b/src/cli/resources/02-backup.json
new file mode 100644
index 0000000..c9bba16
--- /dev/null
+++ b/src/cli/resources/02-backup.json
@@ -0,0 +1,83 @@
+{
+ "continents": [
+ "Europe",
+ "North America",
+ "Testcontinent"
+ ],
+ "backup_state": "USER_ATTRIBUTES_COLLECTING",
+ "selected_continent": "Testcontinent",
+ "countries": [
+ {
+ "code": "xx",
+ "name": "Testland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Testlandt",
+ "de_CH": "Testlandi",
+ "fr": "Testpais",
+ "en": "Testland"
+ },
+ "currency": "TESTKUDOS"
+ },
+ {
+ "code": "xy",
+ "name": "Demoland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Demolandt",
+ "de_CH": "Demolandi",
+ "fr": "Demopais",
+ "en": "Demoland"
+ },
+ "currency": "KUDOS"
+ }
+ ],
+ "authentication_providers": {
+ "http://localhost:8086/": {},
+ "http://localhost:8087/": {},
+ "http://localhost:8088/": {},
+ "http://localhost:8089/": {}
+ },
+ "selected_country": "xx",
+ "currencies": [ "TESTKUDOS" ],
+ "required_attributes": [
+ {
+ "type": "string",
+ "name": "full_name",
+ "label": "Full name",
+ "label_i18n": {
+ "de_DE": "Vollstaendiger Name",
+ "de_CH": "Vollstaendiger Name"
+ },
+ "widget": "anastasis_gtk_ia_full_name"
+ },
+ {
+ "type": "date",
+ "name": "birthdate",
+ "label": "Birthdate",
+ "label_i18n": {
+ "de_CH": "Geburtsdatum"
+ },
+ "widget": "anastasis_gtk_ia_birthdate"
+ },
+ {
+ "type": "string",
+ "name": "sq_number",
+ "label": "Square number",
+ "label_i18n":{
+ "de_DE":"Quadratzahl",
+ "de_CH":"Quadratzahl"
+ },
+ "widget": "anastasis_gtk_xx_square",
+ "uuid" : "ed790bca-89bf-11eb-96f2-233996cf644e",
+ "validation-regex": "^[0-9]+$",
+ "validation-logic": "XX_SQUARE_check"
+ }
+ ]
+}
diff --git a/src/cli/resources/02-recovery.json b/src/cli/resources/02-recovery.json
new file mode 100644
index 0000000..79cfd6d
--- /dev/null
+++ b/src/cli/resources/02-recovery.json
@@ -0,0 +1,83 @@
+{
+ "continents": [
+ "Europe",
+ "North America",
+ "Testcontinent"
+ ],
+ "recovery_state": "USER_ATTRIBUTES_COLLECTING",
+ "selected_continent": "Testcontinent",
+ "countries": [
+ {
+ "code": "xx",
+ "name": "Testland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Testlandt",
+ "de_CH": "Testlandi",
+ "fr": "Testpais",
+ "en": "Testland"
+ },
+ "currency": "TESTKUDOS"
+ },
+ {
+ "code": "xy",
+ "name": "Demoland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Demolandt",
+ "de_CH": "Demolandi",
+ "fr": "Demopais",
+ "en": "Demoland"
+ },
+ "currency": "KUDOS"
+ }
+ ],
+ "authentication_providers": {
+ "http://localhost:8086/": {},
+ "http://localhost:8087/": {},
+ "http://localhost:8088/": {},
+ "http://localhost:8089/": {}
+ },
+ "selected_country": "xx",
+ "currencies": [ "TESTKUDOS" ],
+ "required_attributes": [
+ {
+ "type": "string",
+ "name": "full_name",
+ "label": "Full name",
+ "label_i18n": {
+ "de_DE": "Vollstaendiger Name",
+ "de_CH": "Vollstaendiger Name"
+ },
+ "widget": "anastasis_gtk_ia_full_name"
+ },
+ {
+ "type": "date",
+ "name": "birthdate",
+ "label": "Birthdate",
+ "label_i18n": {
+ "de_CH": "Geburtsdatum"
+ },
+ "widget": "anastasis_gtk_ia_birthdate"
+ },
+ {
+ "type": "string",
+ "name": "sq_number",
+ "label": "Square number",
+ "label_i18n":{
+ "de_DE":"Quadratzahl",
+ "de_CH":"Quadratzahl"
+ },
+ "widget": "anastasis_gtk_xx_square",
+ "uuid" : "ed790bca-89bf-11eb-96f2-233996cf644e",
+ "validation-regex": "^[0-9]+$",
+ "validation-logic": "XX_SQUARE_check"
+ }
+ ]
+}
diff --git a/src/cli/resources/03-backup.json b/src/cli/resources/03-backup.json
new file mode 100644
index 0000000..9d599d7
--- /dev/null
+++ b/src/cli/resources/03-backup.json
@@ -0,0 +1,155 @@
+{
+ "continents": [
+ "Europe",
+ "North America",
+ "Testcontinent"
+ ],
+ "backup_state": "AUTHENTICATIONS_EDITING",
+ "selected_continent": "Testcontinent",
+ "countries": [
+ {
+ "code": "xx",
+ "name": "Testland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Testlandt",
+ "de_CH": "Testlandi",
+ "fr": "Testpais",
+ "en": "Testland"
+ },
+ "currency": "TESTKUDOS"
+ },
+ {
+ "code": "xy",
+ "name": "Demoland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Demolandt",
+ "de_CH": "Demolandi",
+ "fr": "Demopais",
+ "en": "Demoland"
+ },
+ "currency": "KUDOS"
+ }
+ ],
+ "authentication_providers": {
+ "http://localhost:8086/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #1 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "F0HEYJQW81ZAZ3VYMZHFG8T1Z0"
+ },
+ "http://localhost:8087/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #2 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "D378FWXHJB8JHPQFQRZGGV9PWG"
+ },
+ "http://localhost:8088/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #3 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "7W9W4A4TTWSWRPJ76RNDPJHSPR"
+ },
+ "http://localhost:8089/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #4 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "PN0VJF6KDSBYN40SGRCEXPB07M"
+ }
+ },
+ "selected_country": "xx",
+ "currencies": ["TESTKUDOS"],
+ "required_attributes": [
+ {
+ "type": "string",
+ "name": "full_name",
+ "label": "Full name",
+ "label_i18n": {
+ "de_DE": "Vollstaendiger Name",
+ "de_CH": "Vollstaendiger Name"
+ },
+ "widget": "anastasis_gtk_ia_full_name"
+ },
+ {
+ "type": "date",
+ "name": "birthdate",
+ "label": "Birthdate",
+ "label_i18n": {
+ "de_CH": "Geburtsdatum"
+ },
+ "widget": "anastasis_gtk_ia_birthdate"
+ },
+ {
+ "type": "string",
+ "name": "ahv_number",
+ "label": "AHV number",
+ "label_i18n": {
+ "de_DE": "AHV-Nummer",
+ "de_CH": "AHV-Nummer"
+ },
+ "widget": "anastasis_gtk_ia_ahv",
+ "validation-regex": "^(756).[0-9]{4}.[0-9]{4}.[0-9]{2}|(756)[0-9]{10}$",
+ "validation-logic": "CH_AVH_check"
+ }
+ ],
+ "identity_attributes": {
+ "full_name": "Max Musterman",
+ "sq_number": 4,
+ "birthdate": "2000-01-01"
+ }
+}
diff --git a/src/cli/resources/04-backup.json b/src/cli/resources/04-backup.json
new file mode 100644
index 0000000..15c329a
--- /dev/null
+++ b/src/cli/resources/04-backup.json
@@ -0,0 +1,172 @@
+{
+ "continents": [
+ "Europe",
+ "North America",
+ "Testcontinent"
+ ],
+ "backup_state": "AUTHENTICATIONS_EDITING",
+ "selected_continent": "Testcontinent",
+ "countries": [
+ {
+ "code": "xx",
+ "name": "Testland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Testlandt",
+ "de_CH": "Testlandi",
+ "fr": "Testpais",
+ "en": "Testland"
+ },
+ "currency": "TESTKUDOS"
+ },
+ {
+ "code": "xy",
+ "name": "Demoland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Demolandt",
+ "de_CH": "Demolandi",
+ "fr": "Demopais",
+ "en": "Demoland"
+ },
+ "currency": "KUDOS"
+ }
+ ],
+ "authentication_providers": {
+ "http://localhost:8086/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #1 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "F0HEYJQW81ZAZ3VYMZHFG8T1Z0"
+ },
+ "http://localhost:8087/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #2 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "D378FWXHJB8JHPQFQRZGGV9PWG"
+ },
+ "http://localhost:8088/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #3 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "7W9W4A4TTWSWRPJ76RNDPJHSPR"
+ },
+ "http://localhost:8089/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #4 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "PN0VJF6KDSBYN40SGRCEXPB07M"
+ }
+ },
+ "selected_country": "xx",
+ "currencies": [ "TESTKUDOS" ],
+ "required_attributes": [
+ {
+ "type": "string",
+ "name": "full_name",
+ "label": "Full name",
+ "label_i18n": {
+ "de_DE": "Vollstaendiger Name",
+ "de_CH": "Vollstaendiger Name"
+ },
+ "widget": "anastasis_gtk_ia_full_name"
+ },
+ {
+ "type": "date",
+ "name": "birthdate",
+ "label": "Birthdate",
+ "label_i18n": {
+ "de_CH": "Geburtsdatum"
+ },
+ "widget": "anastasis_gtk_ia_birthdate"
+ },
+ {
+ "type": "string",
+ "name": "ahv_number",
+ "label": "AHV number",
+ "label_i18n": {
+ "de_DE": "AHV-Nummer",
+ "de_CH": "AHV-Nummer"
+ },
+ "widget": "anastasis_gtk_ia_ahv",
+ "validation-regex": "^(756).[0-9]{4}.[0-9]{4}.[0-9]{2}|(756)[0-9]{10}$",
+ "validation-logic": "CH_AVH_check"
+ }
+ ],
+ "identity_attributes": {
+ "full_name": "Max Musterman",
+ "sq_number": 4,
+ "birthdate": "2000-01-01"
+ },
+ "authentication_methods": [
+ {
+ "type": "question",
+ "instructions": "What's your name?",
+ "challenge": "Hans"
+ },
+ {
+ "type": "question",
+ "instructions": "What's your X name?",
+ "challenge": "Hansx"
+ },
+ {
+ "type": "question",
+ "instructions": "Where do you live?",
+ "challenge": "Mars"
+ }
+ ]
+}
diff --git a/src/cli/resources/05-backup.json b/src/cli/resources/05-backup.json
new file mode 100644
index 0000000..c0ce8ae
--- /dev/null
+++ b/src/cli/resources/05-backup.json
@@ -0,0 +1,213 @@
+{
+ "continents": [
+ "Europe",
+ "North America",
+ "Testcontinent"
+ ],
+ "backup_state": "POLICIES_REVIEWING",
+ "selected_continent": "Testcontinent",
+ "countries": [
+ {
+ "code": "xx",
+ "name": "Testland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Testlandt",
+ "de_CH": "Testlandi",
+ "fr": "Testpais",
+ "en": "Testland"
+ },
+ "currency": "TESTKUDOS"
+ },
+ {
+ "code": "xy",
+ "name": "Demoland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Demolandt",
+ "de_CH": "Demolandi",
+ "fr": "Demopais",
+ "en": "Demoland"
+ },
+ "currency": "KUDOS"
+ }
+ ],
+ "authentication_providers": {
+ "http://localhost:8086/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #1 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "F0HEYJQW81ZAZ3VYMZHFG8T1Z0"
+ },
+ "http://localhost:8087/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #2 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "D378FWXHJB8JHPQFQRZGGV9PWG"
+ },
+ "http://localhost:8088/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #3 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "7W9W4A4TTWSWRPJ76RNDPJHSPR"
+ },
+ "http://localhost:8089/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #4 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "PN0VJF6KDSBYN40SGRCEXPB07M"
+ }
+ },
+ "selected_country": "xx",
+ "currencies": [ "TESTKUDOS" ],
+ "required_attributes": [
+ {
+ "type": "string",
+ "name": "full_name",
+ "label": "Full name",
+ "label_i18n": {
+ "de_DE": "Vollstaendiger Name",
+ "de_CH": "Vollstaendiger Name"
+ },
+ "widget": "anastasis_gtk_ia_full_name"
+ },
+ {
+ "type": "date",
+ "name": "birthdate",
+ "label": "Birthdate",
+ "label_i18n": {
+ "de_CH": "Geburtsdatum"
+ },
+ "widget": "anastasis_gtk_ia_birthdate"
+ },
+ {
+ "type": "string",
+ "name": "ahv_number",
+ "label": "AHV number",
+ "label_i18n": {
+ "de_DE": "AHV-Nummer",
+ "de_CH": "AHV-Nummer"
+ },
+ "widget": "anastasis_gtk_ia_ahv",
+ "validation-regex": "^(756).[0-9]{4}.[0-9]{4}.[0-9]{2}|(756)[0-9]{10}$",
+ "validation-logic": "CH_AVH_check"
+ }
+ ],
+ "identity_attributes": {
+ "full_name": "Max Musterman",
+ "sq_number": 4,
+ "birthdate": "2000-01-01"
+ },
+ "authentication_methods": [
+ {
+ "type": "question",
+ "instructions": "What's your name?",
+ "challenge": "Hans"
+ },
+ {
+ "type": "question",
+ "instructions": "What's your X name?",
+ "challenge": "Hansx"
+ },
+ {
+ "type": "question",
+ "instructions": "Where do you live?",
+ "challenge": "Mars"
+ }
+ ],
+ "policies": [
+ {
+ "recovery_cost": "TESTKUDOS:0",
+ "methods": [
+ {
+ "authentication_method": 0,
+ "provider": "http://localhost:8089/"
+ },
+ {
+ "authentication_method": 1,
+ "provider": "http://localhost:8088/"
+ }
+ ]
+ },
+ {
+ "recovery_cost": "TESTKUDOS:0",
+ "methods": [
+ {
+ "authentication_method": 0,
+ "provider": "http://localhost:8089/"
+ },
+ {
+ "authentication_method": 2,
+ "provider": "http://localhost:8088/"
+ }
+ ]
+ },
+ {
+ "recovery_cost": "TESTKUDOS:0",
+ "methods": [
+ {
+ "authentication_method": 1,
+ "provider": "http://localhost:8089/"
+ },
+ {
+ "authentication_method": 2,
+ "provider": "http://localhost:8088/"
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/cli/resources/06-backup.json b/src/cli/resources/06-backup.json
new file mode 100644
index 0000000..d1f0b9e
--- /dev/null
+++ b/src/cli/resources/06-backup.json
@@ -0,0 +1,223 @@
+{
+ "continents": [
+ "Europe",
+ "North America",
+ "Testcontinent"
+ ],
+ "backup_state": "SECRET_EDITING",
+ "selected_continent": "Testcontinent",
+ "countries": [
+ {
+ "code": "xx",
+ "name": "Testland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Testlandt",
+ "de_CH": "Testlandi",
+ "fr": "Testpais",
+ "en": "Testland"
+ },
+ "currency": "TESTKUDOS"
+ },
+ {
+ "code": "xy",
+ "name": "Demoland",
+ "continent": "Testcontinent",
+ "continent_i18n": {
+ "xx": "Testkontinent"
+ },
+ "name_i18n": {
+ "de_DE": "Demolandt",
+ "de_CH": "Demolandi",
+ "fr": "Demopais",
+ "en": "Demoland"
+ },
+ "currency": "KUDOS"
+ }
+ ],
+ "authentication_providers": {
+ "http://localhost:8086/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #1 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "F0HEYJQW81ZAZ3VYMZHFG8T1Z0"
+ },
+ "http://localhost:8087/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #2 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "D378FWXHJB8JHPQFQRZGGV9PWG"
+ },
+ "http://localhost:8088/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #3 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "7W9W4A4TTWSWRPJ76RNDPJHSPR"
+ },
+ "http://localhost:8089/": {
+ "methods": [
+ {
+ "type": "question",
+ "usage_fee": "TESTKUDOS:0"
+ }
+ ],
+ "annual_fee": "TESTKUDOS:4.99",
+ "truth_upload_fee": "TESTKUDOS:0.01",
+ "liability_limit": "TESTKUDOS:1",
+ "truth_lifetime": {
+ "d_ms": 63115200000
+ },
+ "currency": "TESTKUDOS",
+ "business_name": "Data loss #4 Inc.",
+ "storage_limit_in_megabytes": 1,
+ "salt": "PN0VJF6KDSBYN40SGRCEXPB07M"
+ }
+ },
+ "selected_country": "xx",
+ "currencies": ["TESTKUDOS"],
+ "required_attributes": [
+ {
+ "type": "string",
+ "name": "full_name",
+ "label": "Full name",
+ "label_i18n": {
+ "de_DE": "Vollstaendiger Name",
+ "de_CH": "Vollstaendiger Name"
+ },
+ "widget": "anastasis_gtk_ia_full_name"
+ },
+ {
+ "type": "date",
+ "name": "birthdate",
+ "label": "Birthdate",
+ "label_i18n": {
+ "de_CH": "Geburtsdatum"
+ },
+ "widget": "anastasis_gtk_ia_birthdate"
+ },
+ {
+ "type": "string",
+ "name": "ahv_number",
+ "label": "AHV number",
+ "label_i18n": {
+ "de_DE": "AHV-Nummer",
+ "de_CH": "AHV-Nummer"
+ },
+ "widget": "anastasis_gtk_ia_ahv",
+ "validation-regex": "^(756).[0-9]{4}.[0-9]{4}.[0-9]{2}|(756)[0-9]{10}$",
+ "validation-logic": "CH_AVH_check"
+ }
+ ],
+ "identity_attributes": {
+ "full_name": "Max Musterman",
+ "ahv_number": "756.9217.0769.85",
+ "birth_year": 2000,
+ "birth_month": 1,
+ "birth_day": 1
+ },
+ "authentication_methods": [
+ {
+ "type": "question",
+ "instructions": "What's your name?",
+ "challenge": "Hans"
+ },
+ {
+ "type": "question",
+ "instructions": "What's your X name?",
+ "challenge": "Hansx"
+ },
+ {
+ "type": "question",
+ "instructions": "Where do you live?",
+ "challenge": "Mars"
+ }
+ ],
+ "policy_providers" : [
+ {
+ "provider_url": "http://localhost:8089/"
+ },
+ {
+ "provider_url": "http://localhost:8089/"
+ }
+ ],
+ "policies": [
+ {
+ "recovery_cost": "TESTKUDOS:0",
+ "methods": [
+ {
+ "authentication_method": 0,
+ "provider": "http://localhost:8089/"
+ },
+ {
+ "authentication_method": 1,
+ "provider": "http://localhost:8088/"
+ }
+ ]
+ },
+ {
+ "recovery_cost": "TESTKUDOS:0",
+ "methods": [
+ {
+ "authentication_method": 0,
+ "provider": "http://localhost:8089/"
+ },
+ {
+ "authentication_method": 2,
+ "provider": "http://localhost:8088/"
+ }
+ ]
+ },
+ {
+ "recovery_cost": "TESTKUDOS:0",
+ "methods": [
+ {
+ "authentication_method": 1,
+ "provider": "http://localhost:8089/"
+ },
+ {
+ "authentication_method": 2,
+ "provider": "http://localhost:8088/"
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/cli/test_anastasis_reducer_1.conf b/src/cli/test_anastasis_reducer_1.conf
new file mode 100644
index 0000000..6a9704d
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_1.conf
@@ -0,0 +1,9 @@
+@INLINE@ test_reducer.conf
+
+[anastasis]
+PORT = 8086
+SERVER_SALT = AUfO1KGOKYIFlFQg
+BUSINESS_NAME = "Data loss #1 Inc."
+
+[stasis-postgres]
+CONFIG = postgres:///anastasischeck1
diff --git a/src/cli/test_anastasis_reducer_2.conf b/src/cli/test_anastasis_reducer_2.conf
new file mode 100644
index 0000000..f909ade
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_2.conf
@@ -0,0 +1,9 @@
+@INLINE@ test_reducer.conf
+
+[anastasis]
+PORT = 8087
+SERVER_SALT = BUfO1KGOKYIFlFQg
+BUSINESS_NAME = "Data loss #2 Inc."
+
+[stasis-postgres]
+CONFIG = postgres:///anastasischeck2
diff --git a/src/cli/test_anastasis_reducer_3.conf b/src/cli/test_anastasis_reducer_3.conf
new file mode 100644
index 0000000..63c38ff
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_3.conf
@@ -0,0 +1,9 @@
+@INLINE@ test_reducer.conf
+
+[anastasis]
+PORT = 8088
+SERVER_SALT = CUfO1KGOKYIFlFQg
+BUSINESS_NAME = "Data loss #3 Inc."
+
+[stasis-postgres]
+CONFIG = postgres:///anastasischeck3
diff --git a/src/cli/test_anastasis_reducer_4.conf b/src/cli/test_anastasis_reducer_4.conf
new file mode 100644
index 0000000..a6d590e
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_4.conf
@@ -0,0 +1,9 @@
+@INLINE@ test_reducer.conf
+
+[anastasis]
+PORT = 8089
+SERVER_SALT = DUfO1KGOKYIFlFQg
+BUSINESS_NAME = "Data loss #4 Inc."
+
+[stasis-postgres]
+CONFIG = postgres:///anastasischeck4
diff --git a/src/cli/test_anastasis_reducer_add_authentication.sh b/src/cli/test_anastasis_reducer_add_authentication.sh
new file mode 100755
index 0000000..7d69076
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_add_authentication.sh
@@ -0,0 +1,134 @@
+#!/bin/bash
+
+set -eu
+
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo " SKIP: $1"
+ exit 77
+}
+
+# Exit, with error message (hard failure)
+function exit_fail() {
+ echo " ERROR: $1"
+ exit 1
+}
+
+# Cleanup to run whenever we exit
+function cleanup()
+{
+ for n in `jobs -p`
+ do
+ kill $n 2> /dev/null || true
+ done
+ rm -f $TFILE $SFILE
+ wait
+}
+
+SFILE=`mktemp test_reducer_stateXXXXXX`
+TFILE=`mktemp test_reducer_stateXXXXXX`
+
+# Install cleanup handler (except for kill -9)
+trap cleanup EXIT
+
+# Check we can actually run
+echo -n "Testing for jq"
+jq -h > /dev/null || exit_skip "jq required"
+echo " FOUND"
+
+echo -n "Testing for anastasis-reducer ..."
+anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
+echo " FOUND"
+
+echo -n "Test add authentication ..."
+
+# First method
+anastasis-reducer -a \
+ '{"authentication_method": {
+ "type": "question",
+ "instructions": "What is your name?",
+ "challenge": "91GPWWR"
+ } }' \
+ add_authentication resources/03-backup.json $TFILE
+
+STATE=`jq -r -e .backup_state < $TFILE`
+if test "$STATE" != "AUTHENTICATIONS_EDITING"
+then
+ exit_fail "Expected new state to be 'AUTHENTICATIONS_EDITING', got '$STATE'"
+fi
+
+ARRAY_LENGTH=`jq -r -e '.authentication_methods | length' < $TFILE`
+if test $ARRAY_LENGTH != 1
+then
+ exit_fail "Expected array length to be 1, got '$ARRAY_LENGTH'"
+fi
+
+echo -n "."
+# Second method
+anastasis-reducer -a \
+ '{"authentication_method": {
+ "type": "question",
+ "instructions": "How old are you?",
+ "challenge": "64S36"
+ }}' \
+ add_authentication $TFILE $SFILE
+
+STATE=`jq -r -e .backup_state < $SFILE`
+if test "$STATE" != "AUTHENTICATIONS_EDITING"
+then
+ exit_fail "Expected new state to be 'AUTHENTICATIONS_EDITING', got '$STATE'"
+fi
+
+ARRAY_LENGTH=`jq -r -e '.authentication_methods | length' < $SFILE`
+if test $ARRAY_LENGTH != 2
+then
+ exit_fail "Expected array length to be 2, got '$ARRAY_LENGTH'"
+fi
+
+echo -n "."
+
+# Third method
+anastasis-reducer -a \
+ '{"authentication_method": {
+ "type": "question",
+ "instructions": "Where do you live?",
+ "challenge": "9NGQ4WR"
+ }}' \
+ add_authentication $SFILE $TFILE
+
+STATE=`jq -r -e .backup_state < $TFILE`
+if test "$STATE" != "AUTHENTICATIONS_EDITING"
+then
+ exit_fail "Expected new state to be 'AUTHENTICATIONS_EDITING', got '$STATE'"
+fi
+
+ARRAY_LENGTH=`jq -r -e '.authentication_methods | length' < $TFILE`
+if test $ARRAY_LENGTH != 3
+then
+ exit_fail "Expected array length to be 3, got '$ARRAY_LENGTH'"
+fi
+
+echo " OK"
+
+
+echo -n "Test delete authentication ..."
+
+anastasis-reducer -a \
+ '{"authentication_method": 2 }' \
+ delete_authentication $TFILE $SFILE
+
+STATE=`jq -r -e .backup_state < $SFILE`
+if test "$STATE" != "AUTHENTICATIONS_EDITING"
+then
+ exit_fail "Expected new state to be 'AUTHENTICATIONS_EDITING', got '$STATE'"
+fi
+
+ARRAY_LENGTH=`jq -r -e '.authentication_methods | length' < $SFILE`
+if test $ARRAY_LENGTH != 2
+then
+ exit_fail "Expected array length to be 2, got '$ARRAY_LENGTH'"
+fi
+
+echo " OK"
+
+exit 0
diff --git a/src/cli/test_anastasis_reducer_backup_enter_user_attributes.sh b/src/cli/test_anastasis_reducer_backup_enter_user_attributes.sh
new file mode 100755
index 0000000..433438e
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_backup_enter_user_attributes.sh
@@ -0,0 +1,140 @@
+#!/bin/bash
+
+set -eu
+
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo " SKIP: $1"
+ exit 77
+}
+
+# Exit, with error message (hard failure)
+function exit_fail() {
+ echo " ERROR: $1"
+ exit 1
+}
+
+# Cleanup to run whenever we exit
+function cleanup()
+{
+ for n in `jobs -p`
+ do
+ kill $n 2> /dev/null || true
+ done
+ wait
+}
+
+CONF_1="test_anastasis_reducer_1.conf"
+CONF_2="test_anastasis_reducer_2.conf"
+CONF_3="test_anastasis_reducer_3.conf"
+CONF_4="test_anastasis_reducer_4.conf"
+TFILE=`mktemp test_reducer_stateXXXXXX`
+
+# Install cleanup handler (except for kill -9)
+trap cleanup EXIT
+
+# Check we can actually run
+echo -n "Testing for jq"
+jq -h > /dev/null || exit_skip "jq required"
+echo " FOUND"
+
+echo -n "Testing for anastasis-reducer ..."
+anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
+echo " FOUND"
+
+echo -n "Testing for anastasis-httpd"
+anastasis-httpd -h >/dev/null </dev/null || exit_skip " MISSING"
+echo " FOUND"
+
+
+# Name of the Postgres database we will use for the script.
+# Will be dropped, do NOT use anything that might be used
+# elsewhere
+TARGET_DB_1=`anastasis-config -c $CONF_1 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+TARGET_DB_2=`anastasis-config -c $CONF_2 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+TARGET_DB_3=`anastasis-config -c $CONF_3 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+TARGET_DB_4=`anastasis-config -c $CONF_4 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+
+echo -n "Initialize anastasis database ..."
+dropdb $TARGET_DB_1 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_1 || exit_skip "Could not create database $TARGET_DB_1"
+anastasis-dbinit -c $CONF_1 2> anastasis-dbinit_1.log
+dropdb $TARGET_DB_2 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_2 || exit_skip "Could not create database $TARGET_DB_2"
+anastasis-dbinit -c $CONF_2 2> anastasis-dbinit_2.log
+dropdb $TARGET_DB_3 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_3 || exit_skip "Could not create database $TARGET_DB_3"
+anastasis-dbinit -c $CONF_3 2> anastasis-dbinit_3.log
+dropdb $TARGET_DB_4 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_4 || exit_skip "Could not create database $TARGET_DB_4"
+anastasis-dbinit -c $CONF_4 2> anastasis-dbinit_4.log
+
+echo " OK"
+
+echo -n "Launching anastasis service ..."
+anastasis-httpd -c $CONF_1 2> anastasis-httpd_1.log &
+anastasis-httpd -c $CONF_2 2> anastasis-httpd_2.log &
+anastasis-httpd -c $CONF_3 2> anastasis-httpd_3.log &
+anastasis-httpd -c $CONF_4 2> anastasis-httpd_4.log &
+
+# Wait for anastasis service to be available
+for n in `seq 1 50`
+do
+ echo -n "."
+ sleep 0.1
+ OK=0
+ # anastasis_01
+ wget http://localhost:8086/ -o /dev/null -O /dev/null >/dev/null || continue
+ # anastasis_02
+ wget http://localhost:8087/ -o /dev/null -O /dev/null >/dev/null || continue
+ # anastasis_03
+ wget http://localhost:8088/ -o /dev/null -O /dev/null >/dev/null || continue
+ # anastasis_04
+ wget http://localhost:8089/ -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+done
+
+if [ 1 != $OK ]
+then
+ exit_skip "Failed to launch anastasis services"
+fi
+echo " OK"
+
+# Test user attributes collection in a backup state
+echo -n "Test user attributes collection in a backup state ..."
+
+anastasis-reducer -L WARNING -a \
+ '{"identity_attributes": {
+ "full_name": "Max Musterman",
+ "sq_number": "4",
+ "birthdate": "2000-01-01"}}' \
+ enter_user_attributes resources/02-backup.json $TFILE
+
+STATE=`jq -r -e .backup_state < $TFILE`
+if test "$STATE" != "AUTHENTICATIONS_EDITING"
+then
+ exit_fail "Expected new state to be 'AUTHENTICATIONS_EDITING', got '$STATE'"
+fi
+
+SELECTED_COUNTRY=`jq -r -e .selected_country < $TFILE`
+if test "$SELECTED_COUNTRY" != "xx"
+then
+ exit_fail "Expected selected country to be 'xx', got '$SELECTED_COUNTRY'"
+fi
+
+echo "OK"
+
+echo -n "Test user attributes collection in a recovery state ..."
+anastasis-reducer -a \
+ '{"identity_attributes": {
+ "full_name": "Max Musterman",
+ "sq_number": "4",
+ "birthdate": "2000-01-01"}}' \
+ enter_user_attributes resources/02-recovery.json $TFILE 2> /dev/null && exit_fail "Expected recovery to fail due to lacking policy data"
+
+echo "OK"
+
+rm -f $TFILE
+
+exit 0
diff --git a/src/cli/test_anastasis_reducer_done_authentication.sh b/src/cli/test_anastasis_reducer_done_authentication.sh
new file mode 100755
index 0000000..87c738c
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_done_authentication.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+
+set -eu
+
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo " SKIP: $1"
+ exit 77
+}
+
+# Exit, with error message (hard failure)
+function exit_fail() {
+ echo " ERROR: $1"
+ exit 1
+}
+
+# Cleanup to run whenever we exit
+function cleanup()
+{
+ for n in `jobs -p`
+ do
+ kill $n 2> /dev/null || true
+ done
+ rm -f $TFILE
+ wait
+}
+
+# Install cleanup handler (except for kill -9)
+TFILE=`mktemp test_reducer_stateXXXXXX`
+trap cleanup EXIT
+
+# Check we can actually run
+echo -n "Testing for jq ..."
+jq -h > /dev/null || exit_skip "jq required"
+echo " FOUND"
+
+echo -n "Testing for anastasis-reducer ..."
+anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
+echo " FOUND"
+
+
+echo -n "Test failing done authentication (next) ..."
+anastasis-reducer next resources/03-backup.json $TFILE 2> /dev/null && exit_fail "Should have failed without challenges"
+
+echo " OK"
+
+
+echo -n "Test done authentication (next) ..."
+anastasis-reducer next resources/04-backup.json $TFILE
+
+STATE=`jq -r -e .backup_state < $TFILE`
+if test "$STATE" != "POLICIES_REVIEWING"
+then
+ exit_fail "Expected new state to be AUTHENTICATIONS_EDITING, got $STATE"
+fi
+
+ARRAY_LENGTH=`jq -r -e '.policies | length' < $TFILE`
+if test $ARRAY_LENGTH -lt 3
+then
+ exit_fail "Expected policy array length to be >= 3, got $ARRAY_LENGTH"
+fi
+
+echo " OK"
+
+exit 0
diff --git a/src/cli/test_anastasis_reducer_done_policy_review.sh b/src/cli/test_anastasis_reducer_done_policy_review.sh
new file mode 100755
index 0000000..7052067
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_done_policy_review.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+set -eu
+
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo " SKIP: $1"
+ exit 77
+}
+
+# Exit, with error message (hard failure)
+function exit_fail() {
+ echo " ERROR: $1"
+ exit 1
+}
+
+# Cleanup to run whenever we exit
+function cleanup()
+{
+ for n in `jobs -p`
+ do
+ kill $n 2> /dev/null || true
+ done
+ rm -f $TFILE
+ wait
+}
+
+# Install cleanup handler (except for kill -9)
+TFILE=`mktemp test_reducer_stateXXXXXX`
+trap cleanup EXIT
+
+
+# Check we can actually run
+echo -n "Testing for jq ..."
+jq -h > /dev/null || exit_skip "jq required"
+echo " FOUND"
+
+echo -n "Testing for anastasis-reducer ..."
+anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
+echo " FOUND"
+
+echo -n "Test done policy review (next) in a backup state ..."
+anastasis-reducer next resources/05-backup.json $TFILE
+
+STATE=`jq -r -e .backup_state < $TFILE`
+if test "$STATE" != "SECRET_EDITING"
+then
+ exit_fail "Expected new state to be 'SECRET_EDITING', got '$STATE'"
+fi
+
+ARRAY_LENGTH=`jq -r -e '.authentication_methods | length' < $TFILE`
+if test $ARRAY_LENGTH -lt 3
+then
+ exit_fail "Expected auth methods array length to be >= 3, got $ARRAY_LENGTH"
+fi
+
+ARRAY_LENGTH=`jq -r -e '.policies | length' < $TFILE`
+if test $ARRAY_LENGTH -lt 3
+then
+ exit_fail "Expected policies array length to be >= 3, got $ARRAY_LENGTH"
+fi
+
+echo " OK"
+
+
+
+echo -n "Test adding policy ..."
+anastasis-reducer -a \
+ '{ "policy" : [
+ { "authentication_method" : 1,
+ "provider" : "http://localhost:8088/" },
+ { "authentication_method" : 1,
+ "provider" : "http://localhost:8089/" }
+ ] }' \
+ add_policy \
+ resources/05-backup.json \
+ $TFILE 2> /dev/null
+
+ARRAY_LENGTH=`jq -r -e '.policies | length' < $TFILE`
+if test $ARRAY_LENGTH -lt 4
+then
+ exit_fail "Expected policy array length to be >= 4, got $ARRAY_LENGTH"
+fi
+
+echo " OK"
+
+
+echo -n "Test deleting policy ..."
+anastasis-reducer -a \
+ '{ "policy_index" : 2 }' \
+ delete_policy \
+ resources/05-backup.json \
+ $TFILE 2> /dev/null
+
+ARRAY_LENGTH=`jq -r -e '.policies | length' < $TFILE`
+if test $ARRAY_LENGTH -ge 3
+then
+ exit_fail "Expected policy array length to be < 3, got $ARRAY_LENGTH"
+fi
+
+echo " OK"
+
+
+
+exit 0
diff --git a/src/cli/test_anastasis_reducer_enter_secret.sh b/src/cli/test_anastasis_reducer_enter_secret.sh
new file mode 100755
index 0000000..dadd8d0
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_enter_secret.sh
@@ -0,0 +1,417 @@
+#!/bin/bash
+## Coloring style Text shell script
+COLOR='\033[0;35m'
+NOCOLOR='\033[0m'
+BOLD="$(tput bold)"
+NORM="$(tput sgr0)"
+
+set -eu
+
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo " SKIP: $1"
+ exit 77
+}
+
+# Exit, with error message (hard failure)
+function exit_fail() {
+ echo " FAIL: $1"
+ exit 1
+}
+
+# Cleanup to run whenever we exit
+function cleanup()
+{
+ for n in `jobs -p`
+ do
+ kill $n 2> /dev/null || true
+ done
+ rm -rf $CONF $WALLET_DB $TFILE $UFILE $TMP_DIR
+ wait
+}
+
+CONF_1="test_anastasis_reducer_1.conf"
+CONF_2="test_anastasis_reducer_2.conf"
+CONF_3="test_anastasis_reducer_3.conf"
+CONF_4="test_anastasis_reducer_4.conf"
+
+# Exchange configuration file will be edited, so we create one
+# from the template.
+CONF=`mktemp test_reducerXXXXXX.conf`
+cp test_reducer.conf $CONF
+
+TMP_DIR=`mktemp -d keys-tmp-XXXXXX`
+WALLET_DB=`mktemp test_reducer_walletXXXXXX.json`
+TFILE=`mktemp test_reducer_statePPXXXXXX`
+UFILE=`mktemp test_reducer_stateBFXXXXXX`
+
+# Install cleanup handler (except for kill -9)
+trap cleanup EXIT
+
+# Check we can actually run
+echo -n "Testing for jq"
+jq -h > /dev/null || exit_skip "jq required"
+echo " FOUND"
+echo -n "Testing for anastasis-reducer ..."
+anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
+echo " FOUND"
+
+echo -n "Testing for taler"
+taler-exchange-httpd -h > /dev/null || exit_skip " taler-exchange required"
+taler-merchant-httpd -h > /dev/null || exit_skip " taler-merchant required"
+echo " FOUND"
+
+echo -n "Testing for taler-bank-manage"
+taler-bank-manage --help >/dev/null </dev/null || exit_skip " MISSING"
+echo " FOUND"
+echo -n "Testing for taler-wallet-cli"
+taler-wallet-cli -v >/dev/null </dev/null || exit_skip " MISSING"
+echo " FOUND"
+
+echo -n "Testing for anastasis-httpd"
+anastasis-httpd -h >/dev/null </dev/null || exit_skip " MISSING"
+echo " FOUND"
+
+echo -n "Initialize anastasis database ..."
+# Name of the Postgres database we will use for the script.
+# Will be dropped, do NOT use anything that might be used
+# elsewhere
+TARGET_DB_1=`anastasis-config -c $CONF_1 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+TARGET_DB_2=`anastasis-config -c $CONF_2 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+TARGET_DB_3=`anastasis-config -c $CONF_3 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+TARGET_DB_4=`anastasis-config -c $CONF_4 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+
+dropdb $TARGET_DB_1 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_1 || exit_skip "Could not create database $TARGET_DB_1"
+anastasis-dbinit -c $CONF_1 2> anastasis-dbinit_1.log
+dropdb $TARGET_DB_2 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_2 || exit_skip "Could not create database $TARGET_DB_2"
+anastasis-dbinit -c $CONF_2 2> anastasis-dbinit_2.log
+dropdb $TARGET_DB_3 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_3 || exit_skip "Could not create database $TARGET_DB_3"
+anastasis-dbinit -c $CONF_3 2> anastasis-dbinit_3.log
+dropdb $TARGET_DB_4 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_4 || exit_skip "Could not create database $TARGET_DB_4"
+anastasis-dbinit -c $CONF_4 2> anastasis-dbinit_4.log
+
+echo " OK"
+
+echo -n "Generating Taler auditor, exchange and merchant configurations ..."
+
+DATA_DIR=`taler-config -f -c $CONF -s PATHS -o TALER_HOME`
+rm -rf $DATA_DIR
+
+# obtain key configuration data
+MASTER_PRIV_FILE=`taler-config -f -c $CONF -s EXCHANGE -o MASTER_PRIV_FILE`
+MASTER_PRIV_DIR=`dirname $MASTER_PRIV_FILE`
+mkdir -p $MASTER_PRIV_DIR
+gnunet-ecc -g1 $MASTER_PRIV_FILE > /dev/null 2> /dev/null
+MASTER_PUB=`gnunet-ecc -p $MASTER_PRIV_FILE`
+EXCHANGE_URL=`taler-config -c $CONF -s EXCHANGE -o BASE_URL`
+MERCHANT_PORT=`taler-config -c $CONF -s MERCHANT -o PORT`
+MERCHANT_URL=http://localhost:${MERCHANT_PORT}/
+BANK_PORT=`taler-config -c $CONF -s BANK -o HTTP_PORT`
+BANK_URL=http://localhost:${BANK_PORT}/
+AUDITOR_URL=http://localhost:8083/
+AUDITOR_PRIV_FILE=`taler-config -f -c $CONF -s AUDITOR -o AUDITOR_PRIV_FILE`
+AUDITOR_PRIV_DIR=`dirname $AUDITOR_PRIV_FILE`
+mkdir -p $AUDITOR_PRIV_DIR
+gnunet-ecc -g1 $AUDITOR_PRIV_FILE > /dev/null 2> /dev/null
+AUDITOR_PUB=`gnunet-ecc -p $AUDITOR_PRIV_FILE`
+
+# patch configuration
+TALER_DB=talercheck
+taler-config -c $CONF -s exchange -o MASTER_PUBLIC_KEY -V $MASTER_PUB
+taler-config -c $CONF -s merchant-exchange-default -o MASTER_KEY -V $MASTER_PUB
+taler-config -c $CONF -s exchangedb-postgres -o CONFIG -V postgres:///$TALER_DB
+taler-config -c $CONF -s auditordb-postgres -o CONFIG -V postgres:///$TALER_DB
+taler-config -c $CONF -s merchantdb-postgres -o CONFIG -V postgres:///$TALER_DB
+taler-config -c $CONF -s bank -o database -V postgres:///$TALER_DB
+taler-config -c $CONF -s exchange -o KEYDIR -V "${TMP_DIR}/keydir/"
+taler-config -c $CONF -s exchange -o REVOCATION_DIR -V "${TMP_DIR}/revdir/"
+
+echo " OK"
+
+echo -n "Setting up exchange ..."
+
+# reset database
+dropdb $TALER_DB >/dev/null 2>/dev/null || true
+createdb $TALER_DB || exit_skip "Could not create database $TALER_DB"
+taler-exchange-dbinit -c $CONF
+taler-merchant-dbinit -c $CONF
+taler-auditor-dbinit -c $CONF
+taler-auditor-exchange -c $CONF -m $MASTER_PUB -u $EXCHANGE_URL
+
+echo " OK"
+
+# Launch services
+echo -n "Launching taler services ..."
+taler-bank-manage-testing $CONF postgres:///$TALER_DB serve > taler-bank.log 2> taler-bank.err &
+taler-exchange-secmod-eddsa -c $CONF 2> taler-exchange-secmod-eddsa.log &
+taler-exchange-secmod-rsa -c $CONF 2> taler-exchange-secmod-rsa.log &
+taler-exchange-httpd -c $CONF 2> taler-exchange-httpd.log &
+taler-merchant-httpd -c $CONF -L INFO 2> taler-merchant-httpd.log &
+taler-exchange-wirewatch -c $CONF 2> taler-exchange-wirewatch.log &
+taler-auditor-httpd -L INFO -c $CONF 2> taler-auditor-httpd.log &
+
+echo " OK"
+
+echo -n "Launching anastasis services ..."
+PREFIX="" #valgrind
+$PREFIX anastasis-httpd -c $CONF_1 2> anastasis-httpd_1.log &
+$PREFIX anastasis-httpd -c $CONF_2 2> anastasis-httpd_2.log &
+$PREFIX anastasis-httpd -c $CONF_3 2> anastasis-httpd_3.log &
+$PREFIX anastasis-httpd -c $CONF_4 2> anastasis-httpd_4.log &
+
+# Wait for bank to be available (usually the slowest)
+for n in `seq 1 50`
+do
+ echo -n "."
+ sleep 0.2
+ OK=0
+ # bank
+ wget --tries=1 --timeout=1 http://localhost:8082/ -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+done
+
+if [ 1 != $OK ]
+then
+ exit_skip "Failed to launch services (bank)"
+fi
+
+# Wait for all other taler services to be available
+for n in `seq 1 50`
+do
+ echo -n "."
+ sleep 0.1
+ OK=0
+ # exchange
+ wget --tries=1 --timeout=1 http://localhost:8081/seed -o /dev/null -O /dev/null >/dev/null || continue
+ # merchant
+ wget --tries=1 --timeout=1 http://localhost:9966/ -o /dev/null -O /dev/null >/dev/null || continue
+ # auditor
+ wget --tries=1 --timeout=1 http://localhost:8083/ -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+done
+
+if [ 1 != $OK ]
+then
+ exit_skip "Failed to launch taler services"
+fi
+
+echo "OK"
+
+echo -n "Setting up keys ..."
+taler-exchange-offline -c $CONF \
+ download \
+ sign \
+ enable-account payto://x-taler-bank/localhost/Exchange \
+ enable-auditor $AUDITOR_PUB $AUDITOR_URL "TESTKUDOS Auditor" \
+ wire-fee now x-taler-bank TESTKUDOS:0.01 TESTKUDOS:0.01 \
+ upload &> taler-exchange-offline.log
+
+echo -n "."
+
+for n in `seq 1 3`
+do
+ echo -n "."
+ OK=0
+ wget --tries=1 --timeout=1 http://localhost:8081/keys -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+done
+
+if [ 1 != $OK ]
+then
+ exit_skip "Failed to setup keys"
+fi
+
+echo " OK"
+
+echo -n "Setting up auditor signatures ..."
+taler-auditor-offline -c $CONF \
+ download sign upload &> taler-auditor-offline.log
+echo " OK"
+
+echo -n "Waiting for anastasis services ..."
+
+# Wait for anastasis services to be available
+for n in `seq 1 50`
+do
+ echo -n "."
+ sleep 0.1
+ OK=0
+ # anastasis_01
+ wget --tries=1 --timeout=1 http://localhost:8086/ -o /dev/null -O /dev/null >/dev/null || continue
+ # anastasis_02
+ wget --tries=1 --timeout=1 http://localhost:8087/ -o /dev/null -O /dev/null >/dev/null || continue
+ # anastasis_03
+ wget --tries=1 --timeout=1 http://localhost:8088/ -o /dev/null -O /dev/null >/dev/null || continue
+ # anastasis_04
+ wget --tries=1 --timeout=1 http://localhost:8089/ -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+done
+
+if [ 1 != $OK ]
+then
+ exit_skip "Failed to launch anastasis services"
+fi
+echo "OK"
+
+echo -n "Configuring merchant instance ..."
+# Setup merchant
+
+curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://x-taler-bank/localhost/43"],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_ms" : 3600000},"default_pay_delay":{"d_ms": 3600000}}' http://localhost:9966/private/instances
+
+
+echo " DONE"
+
+echo -en $COLOR$BOLD"Test enter secret in a backup state ..."$NORM$NOCOLOR
+
+$PREFIX anastasis-reducer -a \
+ '{"secret": { "value" : "veryhardtoguesssecret", "mime" : "text/plain" } }' \
+ enter_secret resources/06-backup.json $TFILE
+
+STATE=`jq -r -e .backup_state < $TFILE`
+if test "$STATE" != "SECRET_EDITING"
+then
+ jq -e . $TFILE
+ exit_fail "Expected new state to be 'SECRET_EDITING', got '$STATE'"
+fi
+
+echo " DONE"
+echo -en $COLOR$BOLD"Test expiration change ..."$NORM$NOCOLOR
+
+MILLIS=`date '+%s'`000
+# Use 156 days into the future to get 1 year
+MILLIS=`expr $MILLIS + 13478400000`
+
+$PREFIX anastasis-reducer -a \
+ "$(jq -n '
+ {"expiration": { "t_ms" : $MSEC } }' \
+ --argjson MSEC $MILLIS
+ )" \
+ update_expiration $TFILE $UFILE
+
+STATE=`jq -r -e .backup_state < $UFILE`
+if test "$STATE" != "SECRET_EDITING"
+then
+ jq -e . $UFILE
+ exit_fail "Expected new state to be 'SECRET_EDITING', got '$STATE'"
+fi
+
+FEES=`jq -r -e '.upload_fees[0].fee' < $UFILE`
+# 4x 4.99 for annual fees, plus 4x0.01 for truth uploads
+if test "$FEES" != "TESTKUDOS:20"
+then
+ jq -e . $TFILE
+ exit_fail "Expected upload fees to be 'TESTKUDOS:20', got '$FEES'"
+fi
+
+
+echo " DONE"
+echo -en $COLOR$BOLD"Test advance to payment ..."$NORM$NOCOLOR
+
+$PREFIX anastasis-reducer next $UFILE $TFILE
+
+STATE=`jq -r -e .backup_state < $TFILE`
+if test "$STATE" != "TRUTHS_PAYING"
+then
+ jq -e . $TFILE
+ exit_fail "Expected new state to be 'TRUTHS_PAYING', got '$STATE'"
+fi
+
+TMETHOD=`jq -r -e '.policies[0].methods[0].truth.type' < $TFILE`
+if test $TMETHOD != "question"
+then
+ exit_fail "Expected method to be >='question', got $TMETHOD"
+fi
+
+echo " OK"
+#Pay
+
+echo -en $COLOR$BOLD"Withdrawing amount to wallet ..."$NORM$NOCOLOR
+
+rm $WALLET_DB
+taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api 'withdrawTestBalance' \
+ "$(jq -n '
+ {
+ amount: "TESTKUDOS:40",
+ bankBaseUrl: $BANK_URL,
+ exchangeBaseUrl: $EXCHANGE_URL
+ }' \
+ --arg BANK_URL "$BANK_URL" \
+ --arg EXCHANGE_URL "$EXCHANGE_URL"
+ )" 2>wallet.err >wallet.log
+taler-wallet-cli --wallet-db=$WALLET_DB run-until-done 2>wallet.err >wallet.log
+
+echo " OK"
+
+echo -en $COLOR$BOLD"Making payments for truth uploads ... "$NORM$NOCOLOR
+OBJECT_SIZE=`jq -r -e '.payments | length' < $TFILE`
+for ((INDEX=0; INDEX < $OBJECT_SIZE; INDEX++))
+do
+ PAY_URI=`jq --argjson INDEX $INDEX -r -e '.payments[$INDEX]' < $TFILE`
+ # run wallet CLI
+ echo -n "$INDEX"
+ taler-wallet-cli --wallet-db=$WALLET_DB handle-uri $PAY_URI -y 2>wallet.err >wallet.log
+ echo -n ","
+done
+echo " OK"
+echo -e $COLOR$BOLD"Running wallet run-pending..."$NORM$NOCOLOR
+taler-wallet-cli --wallet-db=$WALLET_DB run-pending 2>wallet.err >wallet.log
+echo -e $COLOR$BOLD"Payments done"$NORM$NOCOLOR
+
+
+echo -en $COLOR$BOLD"Try to upload again ..."$NORM$NOCOLOR
+$PREFIX anastasis-reducer pay $TFILE $UFILE
+mv $UFILE $TFILE
+echo " OK"
+
+
+STATE=`jq -r -e .backup_state < $TFILE`
+if test "$STATE" != "POLICIES_PAYING"
+then
+ exit_fail "Expected new state to be 'POLICIES_PAYING', got '$STATE'"
+fi
+
+export TFILE
+export UFILE
+
+echo -en $COLOR$BOLD"Making payments for policy uploads ... "$NORM$NOCOLOR
+OBJECT_SIZE=`jq -r -e '.policy_payment_requests | length' < $TFILE`
+for ((INDEX=0; INDEX < $OBJECT_SIZE; INDEX++))
+do
+ PAY_URI=`jq --argjson INDEX $INDEX -r -e '.policy_payment_requests[$INDEX].payto' < $TFILE`
+ # run wallet CLI
+ export PAY_URI
+ echo -n "$INDEX"
+ taler-wallet-cli --wallet-db=$WALLET_DB handle-uri $PAY_URI -y 2>wallet.err >wallet.log
+ echo -n ","
+done
+echo " OK"
+echo -e $COLOR$BOLD"Running wallet run-pending..."$NORM$NOCOLOR
+taler-wallet-cli --wallet-db=$WALLET_DB run-pending 2>wallet.err >wallet.log
+echo -e $COLOR$BOLD"Payments done"$NORM$NOCOLOR
+
+echo -en $COLOR$BOLD"Try to upload again ..."$NORM$NOCOLOR
+$PREFIX anastasis-reducer pay $TFILE $UFILE
+
+echo " OK"
+
+echo -n "Final checks ..."
+
+STATE=`jq -r -e .backup_state < $UFILE`
+if test "$STATE" != "BACKUP_FINISHED"
+then
+ exit_fail "Expected new state to be BACKUP_FINISHED, got $STATE"
+fi
+
+jq -r -e .core_secret < $UFILE > /dev/null && exit_fail "'core_secret' was not cleared upon success"
+
+echo " OK"
+
+exit 0
diff --git a/src/cli/test_anastasis_reducer_initialize_state.sh b/src/cli/test_anastasis_reducer_initialize_state.sh
new file mode 100755
index 0000000..9dc0c59
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_initialize_state.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+set -eu
+
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo " SKIP: $1"
+ exit 77
+}
+
+# Exit, with error message (hard failure)
+function exit_fail() {
+ echo " FAIL: $1"
+ exit 1
+}
+
+# Cleanup to run whenever we exit
+function cleanup()
+{
+ for n in `jobs -p`
+ do
+ kill $n 2> /dev/null || true
+ done
+ rm -f $SFILE $TFILE
+ wait
+}
+
+# Install cleanup handler (except for kill -9)
+SFILE=`mktemp test_reducer_stateXXXXXX`
+TFILE=`mktemp test_reducer_stateXXXXXX`
+
+trap cleanup EXIT
+
+# Check we can actually run
+echo -n "Testing for jq ..."
+jq -h > /dev/null || exit_skip "jq required"
+echo " FOUND"
+echo -n "Testing for anastasis-reducer ..."
+anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
+echo " FOUND"
+echo -n "Test initialization of a backup state ..."
+anastasis-reducer -b $SFILE
+
+STATE=`jq -r -e .backup_state < $SFILE`
+if test "$STATE" != "CONTINENT_SELECTING"
+then
+ exit_fail "Expected initial state to be CONTINENT_SELECTING, got $STATE"
+fi
+jq -e .continents[0] < $SFILE > /dev/null || exit_fail "Expected initial state to include continents"
+
+echo " OK"
+
+echo -n "Test initialization of a recovery state ..."
+anastasis-reducer -r $TFILE
+
+STATE=`jq -r -e .recovery_state < $TFILE`
+if test "$STATE" != "CONTINENT_SELECTING"
+then
+ exit_fail "Expected initial state to be CONTINENT_SELECTING, got $STATE"
+fi
+jq -e .continents[0] < $TFILE > /dev/null || exit_fail "Expected initial state to include continents"
+echo " OK"
+
+exit 0
diff --git a/src/cli/test_anastasis_reducer_recovery_enter_user_attributes.sh b/src/cli/test_anastasis_reducer_recovery_enter_user_attributes.sh
new file mode 100755
index 0000000..d0562e2
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_recovery_enter_user_attributes.sh
@@ -0,0 +1,516 @@
+#!/bin/bash
+
+set -eu
+
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo " SKIP: $1"
+ exit 77
+}
+
+# Exit, with error message (hard failure)
+function exit_fail() {
+ echo " FAIL: $1"
+ exit 1
+}
+
+# Cleanup to run whenever we exit
+function cleanup()
+{
+ for n in `jobs -p`
+ do
+ kill $n 2> /dev/null || true
+ done
+ rm -rf $CONF $WALLET_DB $R1FILE $R2FILE $B1FILE $B2FILE $TMP_DIR
+ wait
+}
+
+
+CONF_1="test_anastasis_reducer_1.conf"
+CONF_2="test_anastasis_reducer_2.conf"
+CONF_3="test_anastasis_reducer_3.conf"
+CONF_4="test_anastasis_reducer_4.conf"
+
+
+# Exchange configuration file will be edited, so we create one
+# from the template.
+CONF=`mktemp test_reducerXXXXXX.conf`
+cp test_reducer.conf $CONF
+
+TMP_DIR=`mktemp -d keys-tmp-XXXXXX`
+WALLET_DB=`mktemp test_reducer_walletXXXXXX.json`
+B1FILE=`mktemp test_reducer_stateB1XXXXXX`
+B2FILE=`mktemp test_reducer_stateB2XXXXXX`
+R1FILE=`mktemp test_reducer_stateR1XXXXXX`
+R2FILE=`mktemp test_reducer_stateR2XXXXXX`
+
+# Install cleanup handler (except for kill -9)
+trap cleanup EXIT
+
+# Check we can actually run
+echo -n "Testing for jq"
+jq -h > /dev/null || exit_skip "jq required"
+echo " FOUND"
+echo -n "Testing for anastasis-reducer ..."
+anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
+echo " FOUND"
+
+echo -n "Testing for taler"
+taler-exchange-httpd -h > /dev/null || exit_skip " taler-exchange required"
+taler-merchant-httpd -h > /dev/null || exit_skip " taler-merchant required"
+echo " FOUND"
+
+echo -n "Testing for taler-bank-manage"
+taler-bank-manage --help >/dev/null </dev/null || exit_skip " MISSING"
+echo " FOUND"
+echo -n "Testing for taler-wallet-cli"
+taler-wallet-cli -v >/dev/null </dev/null || exit_skip " MISSING"
+echo " FOUND"
+
+echo -n "Testing for anastasis-httpd"
+anastasis-httpd -h >/dev/null </dev/null || exit_skip " MISSING"
+echo " FOUND"
+
+echo -n "Initialize anastasis database ..."
+# Name of the Postgres database we will use for the script.
+# Will be dropped, do NOT use anything that might be used
+# elsewhere
+TARGET_DB_1=`anastasis-config -c $CONF_1 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+TARGET_DB_2=`anastasis-config -c $CONF_2 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+TARGET_DB_3=`anastasis-config -c $CONF_3 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+TARGET_DB_4=`anastasis-config -c $CONF_4 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
+
+dropdb $TARGET_DB_1 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_1 || exit_skip "Could not create database $TARGET_DB_1"
+anastasis-dbinit -c $CONF_1 2> anastasis-dbinit_1.log
+dropdb $TARGET_DB_2 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_2 || exit_skip "Could not create database $TARGET_DB_2"
+anastasis-dbinit -c $CONF_2 2> anastasis-dbinit_2.log
+dropdb $TARGET_DB_3 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_3 || exit_skip "Could not create database $TARGET_DB_3"
+anastasis-dbinit -c $CONF_3 2> anastasis-dbinit_3.log
+dropdb $TARGET_DB_4 >/dev/null 2>/dev/null || true
+createdb $TARGET_DB_4 || exit_skip "Could not create database $TARGET_DB_4"
+anastasis-dbinit -c $CONF_4 2> anastasis-dbinit_4.log
+
+echo " OK"
+
+echo -n "Generating Taler auditor, exchange and merchant configurations ..."
+
+DATA_DIR=`taler-config -f -c $CONF -s PATHS -o TALER_HOME`
+rm -rf $DATA_DIR
+
+# obtain key configuration data
+MASTER_PRIV_FILE=`taler-config -f -c $CONF -s EXCHANGE -o MASTER_PRIV_FILE`
+MASTER_PRIV_DIR=`dirname $MASTER_PRIV_FILE`
+mkdir -p $MASTER_PRIV_DIR
+gnunet-ecc -g1 $MASTER_PRIV_FILE > /dev/null 2> /dev/null
+MASTER_PUB=`gnunet-ecc -p $MASTER_PRIV_FILE`
+EXCHANGE_URL=`taler-config -c $CONF -s EXCHANGE -o BASE_URL`
+MERCHANT_PORT=`taler-config -c $CONF -s MERCHANT -o PORT`
+MERCHANT_URL=http://localhost:${MERCHANT_PORT}/
+BANK_PORT=`taler-config -c $CONF -s BANK -o HTTP_PORT`
+BANK_URL=http://localhost:${BANK_PORT}/
+AUDITOR_URL=http://localhost:8083/
+AUDITOR_PRIV_FILE=`taler-config -f -c $CONF -s AUDITOR -o AUDITOR_PRIV_FILE`
+AUDITOR_PRIV_DIR=`dirname $AUDITOR_PRIV_FILE`
+mkdir -p $AUDITOR_PRIV_DIR
+gnunet-ecc -g1 $AUDITOR_PRIV_FILE > /dev/null 2> /dev/null
+AUDITOR_PUB=`gnunet-ecc -p $AUDITOR_PRIV_FILE`
+
+# patch configuration
+TALER_DB=talercheck
+taler-config -c $CONF -s exchange -o MASTER_PUBLIC_KEY -V $MASTER_PUB
+taler-config -c $CONF -s merchant-exchange-default -o MASTER_KEY -V $MASTER_PUB
+taler-config -c $CONF -s exchangedb-postgres -o CONFIG -V postgres:///$TALER_DB
+taler-config -c $CONF -s auditordb-postgres -o CONFIG -V postgres:///$TALER_DB
+taler-config -c $CONF -s merchantdb-postgres -o CONFIG -V postgres:///$TALER_DB
+taler-config -c $CONF -s bank -o database -V postgres:///$TALER_DB
+taler-config -c $CONF -s exchange -o KEYDIR -V "${TMP_DIR}/keydir/"
+taler-config -c $CONF -s exchange -o REVOCATION_DIR -V "${TMP_DIR}/revdir/"
+
+echo " OK"
+
+echo -n "Setting up exchange ..."
+
+# reset database
+dropdb $TALER_DB >/dev/null 2>/dev/null || true
+createdb $TALER_DB || exit_skip "Could not create database $TALER_DB"
+taler-exchange-dbinit -c $CONF
+taler-merchant-dbinit -c $CONF
+taler-auditor-dbinit -c $CONF
+taler-auditor-exchange -c $CONF -m $MASTER_PUB -u $EXCHANGE_URL
+
+echo " OK"
+
+# Launch services
+echo -n "Launching taler services ..."
+taler-bank-manage-testing $CONF postgres:///$TALER_DB serve > taler-bank.log 2> taler-bank.err &
+taler-exchange-secmod-eddsa -c $CONF 2> taler-exchange-secmod-eddsa.log &
+taler-exchange-secmod-rsa -c $CONF 2> taler-exchange-secmod-rsa.log &
+taler-exchange-httpd -c $CONF 2> taler-exchange-httpd.log &
+taler-merchant-httpd -c $CONF -L INFO 2> taler-merchant-httpd.log &
+taler-exchange-wirewatch -c $CONF 2> taler-exchange-wirewatch.log &
+taler-auditor-httpd -L INFO -c $CONF 2> taler-auditor-httpd.log &
+
+echo " OK"
+
+echo -n "Launching anastasis services ..."
+PREFIX="" #valgrind
+$PREFIX anastasis-httpd -c $CONF_1 2> anastasis-httpd_1.log &
+$PREFIX anastasis-httpd -c $CONF_2 2> anastasis-httpd_2.log &
+$PREFIX anastasis-httpd -c $CONF_3 2> anastasis-httpd_3.log &
+$PREFIX anastasis-httpd -c $CONF_4 2> anastasis-httpd_4.log &
+
+# Wait for bank to be available (usually the slowest)
+for n in `seq 1 50`
+do
+ echo -n "."
+ sleep 0.2
+ OK=0
+ # bank
+ wget --tries=1 --timeout=1 http://localhost:8082/ -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+done
+
+if [ 1 != $OK ]
+then
+ exit_skip "Failed to launch services (bank)"
+fi
+
+# Wait for all other taler services to be available
+for n in `seq 1 50`
+do
+ echo -n "."
+ sleep 0.1
+ OK=0
+ # exchange
+ wget --tries=1 --timeout=1 http://localhost:8081/seed -o /dev/null -O /dev/null >/dev/null || continue
+ # merchant
+ wget --tries=1 --timeout=1 http://localhost:9966/ -o /dev/null -O /dev/null >/dev/null || continue
+ # auditor
+ wget --tries=1 --timeout=1 http://localhost:8083/ -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+done
+
+if [ 1 != $OK ]
+then
+ exit_skip "Failed to launch taler services"
+fi
+
+echo "OK"
+
+echo -n "Setting up keys ..."
+taler-exchange-offline -c $CONF \
+ download \
+ sign \
+ enable-account payto://x-taler-bank/localhost/Exchange \
+ enable-auditor $AUDITOR_PUB $AUDITOR_URL "TESTKUDOS Auditor" \
+ wire-fee now x-taler-bank TESTKUDOS:0.01 TESTKUDOS:0.01 \
+ upload &> taler-exchange-offline.log
+
+echo -n "."
+
+for n in `seq 1 3`
+do
+ echo -n "."
+ OK=0
+ wget --tries=1 --timeout=1 http://localhost:8081/keys -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+done
+
+if [ 1 != $OK ]
+then
+ exit_skip "Failed to setup keys"
+fi
+
+echo " OK"
+
+echo -n "Setting up auditor signatures ..."
+taler-auditor-offline -c $CONF \
+ download sign upload &> taler-auditor-offline.log
+echo " OK"
+
+echo -n "Waiting for anastasis services ..."
+
+# Wait for anastasis services to be available
+for n in `seq 1 50`
+do
+ echo -n "."
+ sleep 0.1
+ OK=0
+ # anastasis_01
+ wget --tries=1 --timeout=1 http://localhost:8086/ -o /dev/null -O /dev/null >/dev/null || continue
+ # anastasis_02
+ wget --tries=1 --timeout=1 http://localhost:8087/ -o /dev/null -O /dev/null >/dev/null || continue
+ # anastasis_03
+ wget --tries=1 --timeout=1 http://localhost:8088/ -o /dev/null -O /dev/null >/dev/null || continue
+ # anastasis_04
+ wget --tries=1 --timeout=1 http://localhost:8089/ -o /dev/null -O /dev/null >/dev/null || continue
+ OK=1
+ break
+done
+
+if [ 1 != $OK ]
+then
+ exit_skip "Failed to launch anastasis services"
+fi
+echo "OK"
+
+echo -n "Configuring merchant instance ..."
+# Setup merchant
+
+curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://x-taler-bank/localhost/43"],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_ms" : 3600000},"default_pay_delay":{"d_ms": 3600000}}' http://localhost:9966/private/instances
+
+
+echo " DONE"
+
+echo -n "Running backup logic ...,"
+anastasis-reducer -b > $B1FILE
+echo -n "."
+anastasis-reducer -a \
+ '{"continent": "Testcontinent"}' \
+ select_continent < $B1FILE > $B2FILE
+echo -n "."
+anastasis-reducer -a \
+ '{"country_code": "xx",
+ "currencies":["TESTKUDOS"]}' \
+ select_country < $B2FILE > $B1FILE
+echo -n "."
+anastasis-reducer -a \
+ '{"identity_attributes": {
+ "full_name": "Max Musterman",
+ "sq_number": "4",
+ "birthdate": "2000-01-01"}}' \
+ enter_user_attributes < $B1FILE > $B2FILE
+echo -n ","
+# "91GPWWR" encodes "Hans"
+anastasis-reducer -a \
+ '{"authentication_method": {
+ "type": "question",
+ "instructions": "What is your name?",
+ "challenge": "91GPWWR"
+ } }' \
+ add_authentication < $B2FILE > $B1FILE
+echo -n "."
+# "64S36" encodes "123"
+anastasis-reducer -a \
+ '{"authentication_method": {
+ "type": "question",
+ "instructions": "How old are you?",
+ "challenge": "64S36"
+ } }' \
+ add_authentication < $B1FILE > $B2FILE
+echo -n "."
+# "9NGQ4WR" encodes "Mars"
+anastasis-reducer -a \
+ '{"authentication_method": {
+ "type": "question",
+ "instructions": "Where do you live?",
+ "challenge": "9NGQ4WR"
+ } }' \
+ add_authentication < $B2FILE > $B1FILE
+echo -n "."
+# Finished adding authentication methods
+anastasis-reducer \
+ next < $B1FILE > $B2FILE
+
+
+echo -n ","
+# Finished policy review
+anastasis-reducer \
+ next < $B2FILE > $B1FILE
+echo -n "."
+
+# Note: 'secret' must here be a Crockford base32-encoded value
+anastasis-reducer -a \
+ '{"secret": { "value" : "VERYHARDT0GVESSSECRET", "mime" : "text/plain" }}' \
+ enter_secret < $B1FILE > $B2FILE
+mv $B2FILE $B1FILE
+anastasis-reducer next $B1FILE $B2FILE
+echo " OK"
+
+
+echo -n "Preparing wallet"
+rm $WALLET_DB
+taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api 'withdrawTestBalance' \
+ "$(jq -n '
+ {
+ amount: "TESTKUDOS:100",
+ bankBaseUrl: $BANK_URL,
+ exchangeBaseUrl: $EXCHANGE_URL
+ }' \
+ --arg BANK_URL "$BANK_URL" \
+ --arg EXCHANGE_URL "$EXCHANGE_URL"
+ )" 2> /dev/null >/dev/null
+taler-wallet-cli --wallet-db=$WALLET_DB run-until-done 2>/dev/null >/dev/null
+echo " OK"
+
+echo -en "Making payments for truth uploads ... "
+OBJECT_SIZE=`jq -r -e '.payments | length' < $B2FILE`
+for ((INDEX=0; INDEX < $OBJECT_SIZE; INDEX++))
+do
+ PAY_URI=`jq --argjson INDEX $INDEX -r -e '.payments[$INDEX]' < $B2FILE`
+ # run wallet CLI
+ echo -n "$INDEX"
+ taler-wallet-cli --wallet-db=$WALLET_DB handle-uri $PAY_URI -y 2>/dev/null >/dev/null
+ echo -n ", "
+done
+echo "OK"
+echo -e "Running wallet run-pending..."
+taler-wallet-cli --wallet-db=$WALLET_DB run-pending 2>/dev/null >/dev/null
+echo -e "Payments done"
+
+export B2FILE
+export B1FILE
+
+echo -en "Try to upload again ..."
+$PREFIX anastasis-reducer pay $B2FILE $B1FILE
+mv $B1FILE $B2FILE
+echo " OK"
+
+echo -en "Making payments for policy uploads ... "
+OBJECT_SIZE=`jq -r -e '.policy_payment_requests | length' < $B2FILE`
+for ((INDEX=0; INDEX < $OBJECT_SIZE; INDEX++))
+do
+ PAY_URI=`jq --argjson INDEX $INDEX -r -e '.policy_payment_requests[$INDEX].payto' < $B2FILE`
+ # run wallet CLI
+ echo -n "$INDEX"
+ taler-wallet-cli --wallet-db=$WALLET_DB handle-uri $PAY_URI -y 2>/dev/null >/dev/null
+ echo -n ", "
+done
+echo " OK"
+echo -en "Running wallet run-pending..."
+taler-wallet-cli --wallet-db=$WALLET_DB run-pending 2>/dev/null >/dev/null
+echo -e " payments DONE"
+
+echo -en "Try to upload again ..."
+anastasis-reducer \
+ pay < $B2FILE > $B1FILE
+echo " OK: Backup finished"
+echo -n "Final backup checks ..."
+STATE=`jq -r -e .backup_state < $B1FILE`
+if test "$STATE" != "BACKUP_FINISHED"
+then
+ exit_fail "Expected new state to be 'BACKUP_FINISHED', got '$STATE'"
+fi
+
+jq -r -e .core_secret < $B1FILE > /dev/null && exit_fail "'core_secret' was not cleared upon success"
+
+echo " OK"
+
+echo -n "Running recovery basic logic ..."
+anastasis-reducer -r > $R1FILE
+anastasis-reducer -a \
+ '{"continent": "Testcontinent"}' \
+ select_continent < $R1FILE > $R2FILE
+anastasis-reducer -a \
+ '{"country_code": "xx",
+ "currencies":["TESTKUDOS"]}' \
+ select_country < $R2FILE > $R1FILE
+anastasis-reducer -a '{"identity_attributes": { "full_name": "Max Musterman", "sq_number": "4", "birthdate": "2000-01-01" }}' enter_user_attributes < $R1FILE > $R2FILE
+
+
+STATE=`jq -r -e .recovery_state < $R2FILE`
+if test "$STATE" != "SECRET_SELECTING"
+then
+ exit_fail "Expected new state to be 'SECRET_SELECTING', got '$STATE'"
+fi
+echo " OK"
+
+echo -n "Selecting default secret"
+mv $R2FILE $R1FILE
+anastasis-reducer next < $R1FILE > $R2FILE
+
+STATE=`jq -r -e .recovery_state < $R2FILE`
+if test "$STATE" != "CHALLENGE_SELECTING"
+then
+ exit_fail "Expected new state to be 'CHALLENGE_SELECTING', got '$STATE'"
+fi
+echo " OK"
+
+echo -n "Running challenge logic ..."
+
+UUID0=`jq -r -e .recovery_information.challenges[0].uuid < $R2FILE`
+UUID1=`jq -r -e .recovery_information.challenges[1].uuid < $R2FILE`
+UUID2=`jq -r -e .recovery_information.challenges[2].uuid < $R2FILE`
+UUID0Q=`jq -r -e .recovery_information.challenges[0].instructions < $R2FILE`
+UUID1Q=`jq -r -e .recovery_information.challenges[1].instructions < $R2FILE`
+UUID2Q=`jq -r -e .recovery_information.challenges[2].instructions < $R2FILE`
+
+if test "$UUID2Q" = 'How old are you?'
+then
+ AGE_UUID=$UUID2
+elif test "$UUID1Q" = 'How old are you?'
+then
+ AGE_UUID=$UUID1
+else
+ AGE_UUID=$UUID0
+fi
+
+if test "$UUID2Q" = 'What is your name?'
+then
+ NAME_UUID=$UUID2
+elif test "$UUID1Q" = 'What is your name?'
+then
+ NAME_UUID=$UUID1
+else
+ NAME_UUID=$UUID0
+fi
+
+anastasis-reducer -a \
+ "$(jq -n '
+ {
+ uuid: $UUID
+ }' \
+ --arg UUID "$NAME_UUID"
+ )" \
+ select_challenge < $R2FILE > $R1FILE
+
+anastasis-reducer -a '{"answer": "Hans"}' \
+ solve_challenge < $R1FILE > $R2FILE
+
+anastasis-reducer -a \
+ "$(jq -n '
+ {
+ uuid: $UUID
+ }' \
+ --arg UUID "$AGE_UUID"
+ )" \
+ select_challenge < $R2FILE > $R1FILE
+
+anastasis-reducer -a '{"answer": "123"}' \
+ solve_challenge < $R1FILE > $R2FILE
+
+echo " OK"
+
+echo -n "Checking recovered secret ..."
+# finally: check here that we recovered the secret...
+
+STATE=`jq -r -e .recovery_state < $R2FILE`
+if test "$STATE" != "RECOVERY_FINISHED"
+then
+ jq -e . $R2FILE
+ exit_fail "Expected new state to be 'RECOVERY_FINISHED', got '$STATE'"
+fi
+
+SECRET=`jq -r -e .core_secret.value < $R2FILE`
+if test "$SECRET" != "VERYHARDT0GVESSSECRET"
+then
+ jq -e . $R2FILE
+ exit_fail "Expected recovered secret to be 'VERYHARDT0GVESSSECRET', got '$SECRET'"
+fi
+
+MIME=`jq -r -e .core_secret.mime < $R2FILE`
+if test "$MIME" != "text/plain"
+then
+ jq -e . $R2FILE
+ exit_fail "Expected recovered mime to be 'text/plain', got '$MIME'"
+fi
+
+echo " OK"
+
+exit 0
diff --git a/src/cli/test_anastasis_reducer_select_continent.sh b/src/cli/test_anastasis_reducer_select_continent.sh
new file mode 100755
index 0000000..4cd8a84
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_select_continent.sh
@@ -0,0 +1,116 @@
+#!/bin/bash
+
+set -eu
+
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo " SKIP: $1"
+ exit 77
+}
+
+# Exit, with error message (hard failure)
+function exit_fail() {
+ echo " FAIL: $1"
+ exit 1
+}
+
+# Cleanup to run whenever we exit
+function cleanup()
+{
+ for n in `jobs -p`
+ do
+ kill $n 2> /dev/null || true
+ done
+ rm -f $SFILE $TFILE
+ wait
+}
+
+# Install cleanup handler (except for kill -9)
+SFILE=`mktemp test_reducer_stateXXXXXX`
+TFILE=`mktemp test_reducer_stateXXXXXX`
+
+trap cleanup EXIT
+
+# Check we can actually run
+echo -n "Testing for jq ..."
+jq -h > /dev/null || exit_skip "jq required"
+echo " FOUND"
+echo -n "Testing for anastasis-reducer ..."
+anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
+echo " FOUND"
+
+# Test continent selection in a backup state
+echo -n "Test continent selection in a backup state ..."
+anastasis-reducer -a '{"continent": "Testcontinent"}' select_continent resources/00-backup.json $TFILE
+
+STATE=`jq -r -e .backup_state < $TFILE`
+if test "$STATE" != "COUNTRY_SELECTING"
+then
+ exit_fail "Expected new state to be COUNTRY_SELECTING, got $STATE"
+fi
+SELECTED_CONTINENT=`jq -r -e .selected_continent < $TFILE`
+if test "$SELECTED_CONTINENT" != "Testcontinent"
+then
+ exit_fail "Expected selected continent to be Testcontinent, got $SELECTED_CONTINENT"
+fi
+COUNTRIES=`jq -r -e .countries < $TFILE`
+if test "$COUNTRIES" == NULL
+then
+ exit_fail "Expected country array (countries) not to be NULL"
+fi
+echo " OK"
+
+
+echo -n "Test invalid continent selection ..."
+anastasis-reducer -a '{"continent": "Pangaia"}' select_continent resources/00-recovery.json $TFILE 2> /dev/null \
+ && exit_fail "Expected selection to fail. Check '$TFILE'"
+
+echo " OK"
+
+echo -n "Test continent selection in a recovery state ..."
+anastasis-reducer -a '{"continent": "Testcontinent"}' select_continent resources/00-recovery.json $TFILE
+
+STATE=`jq -r -e .recovery_state < $TFILE`
+if test "$STATE" != "COUNTRY_SELECTING"
+then
+ exit_fail "Expected new state to be COUNTRY_SELECTING, got $STATE"
+fi
+jq -e .countries[0] < $TFILE > /dev/null || exit_fail "Expected new state to include countries"
+jq -e .countries[0].code < $TFILE > /dev/null || exit_fail "Expected new state to include countries with code"
+jq -e .countries[0].continent < $TFILE > /dev/null || exit_fail "Expected new state to include countries with continent"
+jq -e .countries[0].name < $TFILE > /dev/null || exit_fail "Expected new state to include countries with name"
+jq -e .countries[0].currency < $TFILE > /dev/null || exit_fail "Expected new state to include countries with currency"
+
+SELECTED_CONTINENT=`jq -r -e .selected_continent < $TFILE`
+if test "$SELECTED_CONTINENT" != "Testcontinent"
+then
+ exit_fail "Expected selected continent to be 'Testcontinent', got $SELECTED_CONTINENT"
+fi
+
+COUNTRIES=`jq -r -e .countries < $TFILE`
+if test "$COUNTRIES" == NULL
+then
+ exit_fail "Expected country array (countries) not to be NULL"
+fi
+jq -e .countries[0] < $TFILE > /dev/null || exit_fail "Expected new state to include countries"
+jq -e .countries[0].code < $TFILE > /dev/null || exit_fail "Expected new state to include countries with code"
+jq -e .countries[0].continent < $TFILE > /dev/null || exit_fail "Expected new state to include countries with continent"
+jq -e .countries[0].name < $TFILE > /dev/null || exit_fail "Expected new state to include countries with name"
+jq -e .countries[0].currency < $TFILE > /dev/null || exit_fail "Expected new state to include countries with currency"
+
+echo " OK"
+
+
+# Test missing arguments in a recovery state
+echo -n "Test bogus country selection in a recovery state ..."
+anastasis-reducer -a '{"country": "Germany"}' select_continent resources/00-recovery.json $TFILE 2> /dev/null && exit_fail "Expected state transition to fail, but it worked, check $TFILE"
+
+echo " OK"
+
+# Test continent selection in a recovery state
+echo -n "Test bogus continent selection in a recovery state ..."
+anastasis-reducer -a '{"continent": "Germany"}' select_continent resources/00-recovery.json $TFILE 2> /dev/null && exit_fail "Expected state transition to fail, but it worked, check $TFILE"
+
+echo " OK"
+
+exit 0
diff --git a/src/cli/test_anastasis_reducer_select_country.sh b/src/cli/test_anastasis_reducer_select_country.sh
new file mode 100755
index 0000000..db17052
--- /dev/null
+++ b/src/cli/test_anastasis_reducer_select_country.sh
@@ -0,0 +1,144 @@
+#!/bin/bash
+
+set -eu
+
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo " SKIP: $1"
+ exit 77
+}
+
+# Exit, with error message (hard failure)
+function exit_fail() {
+ echo " FAIL: $1"
+ exit 1
+}
+
+# Cleanup to run whenever we exit
+function cleanup()
+{
+ for n in `jobs -p`
+ do
+ kill $n 2> /dev/null || true
+ done
+ rm -f $TFILE
+ wait
+}
+
+
+
+TFILE=`mktemp test_reducer_stateXXXXXX`
+
+# Install cleanup handler (except for kill -9)
+trap cleanup EXIT
+
+# Check we can actually run
+echo -n "Testing for jq"
+jq -h > /dev/null || exit_skip "jq required"
+echo " FOUND"
+
+echo -n "Testing for anastasis-reducer ..."
+anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
+echo " FOUND"
+
+
+
+# Test continent re-selection
+echo -n "Test continent re-selection ..."
+anastasis-reducer -a '{"continent": "Europe"}' select_continent resources/01-recovery.json $TFILE
+
+echo -n "."
+
+
+STATE=`jq -r -e .recovery_state < $TFILE`
+if test "$STATE" != "COUNTRY_SELECTING"
+then
+ exit_fail "Expected new state to be COUNTRY_SELECTING, got $STATE"
+fi
+
+echo -n "."
+
+jq -e .countries[0] < $TFILE > /dev/null || exit_fail "Expected new state to include countries"
+jq -e .countries[0].code < $TFILE > /dev/null || exit_fail "Expected new state to include countries with code"
+jq -e .countries[0].continent < $TFILE > /dev/null || exit_fail "Expected new state to include countries with continent"
+jq -e .countries[0].name < $TFILE > /dev/null || exit_fail "Expected new state to include countries with name"
+jq -e .countries[0].currency < $TFILE > /dev/null || exit_fail "Expected new state to include countries with currency"
+
+SELECTED_CONTINENT=`jq -r -e .selected_continent < $TFILE`
+if test "$SELECTED_CONTINENT" != "Europe"
+then
+ exit_fail "Expected selected continent to be 'Testcontinent', got $SELECTED_CONTINENT"
+fi
+
+echo " OK"
+
+
+echo -n "Test invalid continent re-selection ..."
+anastasis-reducer -a '{"continent": "Pangaia"}' select_continent resources/00-recovery.json $TFILE 2> /dev/null \
+ && exit_fail "Expected selection to fail. Check '$TFILE'"
+
+echo " OK"
+
+
+echo -n "Test NX country selection ..."
+
+anastasis-reducer -a \
+ '{"country_code": "zz",
+ "currencies": ["EUR" ]}' \
+ select_country \
+ resources/01-backup.json $TFILE 2> /dev/null \
+ && exit_fail "Expected selection to fail. Check '$TFILE'"
+
+echo " OK"
+
+echo -n "Test invalid country selection for continent ..."
+
+anastasis-reducer -a \
+ '{"country_code": "de",
+ "currencies":["EUR"]}' \
+ select_country \
+ resources/01-backup.json $TFILE 2> /dev/null \
+ && exit_fail "Expected selection to fail. Check '$TFILE'"
+
+echo " OK"
+
+echo -n "Test country selection ..."
+
+anastasis-reducer -a \
+ '{"country_code": "xx",
+ "currencies":["TESTKUDOS"]}' \
+ select_country resources/01-backup.json $TFILE
+
+STATE=`jq -r -e .backup_state < $TFILE`
+if test "$STATE" != "USER_ATTRIBUTES_COLLECTING"
+then
+ exit_fail "Expected new state to be 'USER_ATTRIBUTES_COLLECTING', got '$STATE'"
+fi
+echo -n "."
+SELECTED_COUNTRY=`jq -r -e .selected_country < $TFILE`
+if test "$SELECTED_COUNTRY" != "xx"
+then
+ exit_fail "Expected selected country to be 'xx', got '$SELECTED_COUNTRY'"
+fi
+echo -n "."
+SELECTED_CURRENCY=`jq -r -e .currencies[0] < $TFILE`
+if test "$SELECTED_CURRENCY" != "TESTKUDOS"
+then
+ exit_fail "Expected selected currency to be 'TESTKUDOS', got '$SELECTED_CURRENCY'"
+fi
+echo -n "."
+REQ_ATTRIBUTES=`jq -r -e .required_attributes < $TFILE`
+if test "$REQ_ATTRIBUTES" == NULL
+then
+ exit_fail "Expected required attributes array not to be NULL"
+fi
+echo -n "."
+AUTH_PROVIDERS=`jq -r -e .authentication_providers < $TFILE`
+if test "$AUTH_PROVIDERS" == NULL
+then
+ exit_fail "Expected authentication_providers array not to be NULL"
+fi
+
+echo " OK"
+
+exit 0
diff --git a/src/cli/test_reducer.conf b/src/cli/test_reducer.conf
new file mode 100644
index 0000000..a4baaed
--- /dev/null
+++ b/src/cli/test_reducer.conf
@@ -0,0 +1,197 @@
+[PATHS]
+TALER_HOME = ${PWD}/test_reducer_home/
+TALER_DATA_HOME = $TALER_HOME/.local/share/taler/
+TALER_CONFIG_HOME = $TALER_HOME/.config/taler/
+TALER_CACHE_HOME = $TALER_HOME/.cache/taler/
+TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/
+
+[taler]
+CURRENCY = TESTKUDOS
+CURRENCY_ROUND_UNIT = TESTKUDOS:0.01
+
+[anastasis]
+DB = postgres
+PAYMENT_BACKEND_URL = http://localhost:9966/
+ANNUAL_FEE = TESTKUDOS:4.99
+TRUTH_UPLOAD_FEE = TESTKUDOS:0.01
+UPLOAD_LIMIT_MB = 1
+ANNUAL_POLICY_UPLOAD_LIMIT = 128
+INSURANCE = TESTKUDOS:1.0
+
+[authorization-question]
+COST = TESTKUDOS:0.0
+
+
+[exchange]
+MAX_KEYS_CACHING = forever
+DB = postgres
+MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
+SERVE = tcp
+UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http
+UNIXPATH_MODE = 660
+PORT = 8081
+BASE_URL = http://localhost:8081/
+SIGNKEY_DURATION = 2 weeks
+SIGNKEY_LEGAL_DURATION = 2 years
+LEGAL_DURATION = 2 years
+LOOKAHEAD_SIGN = 3 weeks 1 day
+LOOKAHEAD_PROVIDE = 2 weeks 1 day
+KEYDIR = ${TALER_DATA_HOME}/exchange/live-keys/
+REVOCATION_DIR = ${TALER_DATA_HOME}/exchange/revocations/
+TERMS_ETAG = 0
+PRIVACY_ETAG = 0
+
+[merchant]
+SERVE = tcp
+PORT = 9966
+UNIXPATH = ${TALER_RUNTIME_DIR}/merchant.http
+UNIXPATH_MODE = 660
+DEFAULT_WIRE_FEE_AMORTIZATION = 1
+DB = postgres
+WIREFORMAT = default
+# Set very low, so we can be sure that the database generated
+# will contain wire transfers "ready" for the aggregator.
+WIRE_TRANSFER_DELAY = 1 minute
+DEFAULT_PAY_DEADLINE = 1 day
+DEFAULT_MAX_DEPOSIT_FEE = TESTKUDOS:0.1
+KEYFILE = ${TALER_DATA_HOME}/merchant/merchant.priv
+DEFAULT_MAX_WIRE_FEE = TESTKUDOS:0.10
+
+# Ensure that merchant reports EVERY deposit confirmation to auditor
+FORCE_AUDIT = YES
+
+[auditor]
+DB = postgres
+AUDITOR_PRIV_FILE = ${TALER_DATA_HOME}/auditor/offline-keys/auditor.priv
+SERVE = tcp
+UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http
+UNIXPATH_MODE = 660
+PORT = 8083
+AUDITOR_URL = http://localhost:8083/
+TINY_AMOUNT = TESTKUDOS:0.01
+AUDITOR_PRIV_FILE = ${TALER_DATA_HOME}/auditor/offline-keys/auditor.priv
+BASE_URL = "http://localhost:8083/"
+
+[bank]
+DATABASE = postgres:///taler-auditor-basedb
+MAX_DEBT = TESTKUDOS:50.0
+MAX_DEBT_BANK = TESTKUDOS:100000.0
+HTTP_PORT = 8082
+SUGGESTED_EXCHANGE = http://localhost:8081/
+SUGGESTED_EXCHANGE_PAYTO = payto://x-taler-bank/localhost/2
+ALLOW_REGISTRATIONS = YES
+SERVE = http
+
+[exchangedb]
+IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
+LEGAL_RESERVE_EXPIRATION_TIME = 7 years
+
+[exchange-account-1]
+PAYTO_URI = payto://x-taler-bank/localhost/Exchange
+enable_debit = yes
+enable_credit = yes
+WIRE_GATEWAY_URL = "http://localhost:8082/taler-wire-gateway/Exchange/"
+WIRE_GATEWAY_AUTH_METHOD = basic
+USERNAME = Exchange
+PASSWORD = x
+
+[merchant-exchange-default]
+EXCHANGE_BASE_URL = http://localhost:8081/
+CURRENCY = TESTKUDOS
+
+[payments-generator]
+currency = TESTKUDOS
+instance = default
+bank = http://localhost:8082/
+merchant = http://localhost:9966/
+exchange_admin = http://localhost:18080/
+exchange-admin = http://localhost:18080/
+exchange = http://localhost:8081/
+
+[coin_kudos_ct_1]
+value = TESTKUDOS:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.01
+fee_deposit = TESTKUDOS:0.01
+fee_refresh = TESTKUDOS:0.01
+fee_refund = TESTKUDOS:0.01
+rsa_keysize = 1024
+
+[coin_kudos_ct_10]
+value = TESTKUDOS:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.01
+fee_deposit = TESTKUDOS:0.01
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.01
+rsa_keysize = 1024
+
+[coin_kudos_1]
+value = TESTKUDOS:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.02
+fee_deposit = TESTKUDOS:0.02
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.01
+rsa_keysize = 1024
+
+[coin_kudos_2]
+value = TESTKUDOS:2
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.03
+fee_deposit = TESTKUDOS:0.03
+fee_refresh = TESTKUDOS:0.04
+fee_refund = TESTKUDOS:0.02
+rsa_keysize = 1024
+
+[coin_kudos_4]
+value = TESTKUDOS:4
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.03
+fee_deposit = TESTKUDOS:0.03
+fee_refresh = TESTKUDOS:0.04
+fee_refund = TESTKUDOS:0.02
+rsa_keysize = 1024
+
+[coin_kudos_5]
+value = TESTKUDOS:5
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.01
+fee_deposit = TESTKUDOS:0.01
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.01
+rsa_keysize = 1024
+
+[coin_kudos_8]
+value = TESTKUDOS:8
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.05
+fee_deposit = TESTKUDOS:0.02
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.04
+rsa_keysize = 1024
+
+[coin_kudos_10]
+value = TESTKUDOS:10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = TESTKUDOS:0.01
+fee_deposit = TESTKUDOS:0.01
+fee_refresh = TESTKUDOS:0.03
+fee_refund = TESTKUDOS:0.01
+rsa_keysize = 1024
diff --git a/src/cli/user-details-example.json b/src/cli/user-details-example.json
new file mode 100644
index 0000000..3eb1f7a
--- /dev/null
+++ b/src/cli/user-details-example.json
@@ -0,0 +1,6 @@
+{
+ "first-name":"John",
+ "last-name":"Wayne",
+ "birthdate":"01-01-1901",
+ "social-security-number":"123456789"
+} \ No newline at end of file