add new subcommands for AML staff management and partner exchanges

This commit is contained in:
Christian Grothoff 2023-01-22 19:40:47 +01:00
parent d53dd753e1
commit c2eee251c2
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
2 changed files with 617 additions and 2 deletions

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2020, 2021, 2022 Taler Systems SA Copyright (C) 2020-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software terms of the GNU General Public License as published by the Free Software
@ -112,6 +112,16 @@
*/ */
#define OP_DRAIN_PROFITS "exchange-drain-profits-0" #define OP_DRAIN_PROFITS "exchange-drain-profits-0"
/**
* Setup AML staff.
*/
#define OP_UPDATE_AML_STAFF "exchange-add-aml-staff-0"
/**
* Setup partner exchange for wad transfers.
*/
#define OP_ADD_PARTNER "exchange-add-partner-0"
/** /**
* Our private key, initialized in #load_offline_key(). * Our private key, initialized in #load_offline_key().
*/ */
@ -498,6 +508,62 @@ struct UploadExtensionsRequest
}; };
/**
* Data structure for AML staff requests.
*/
struct AmlStaffRequest
{
/**
* Kept in a DLL.
*/
struct AmlStaffRequest *next;
/**
* Kept in a DLL.
*/
struct AmlStaffRequest *prev;
/**
* Operation handle.
*/
struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *h;
/**
* Array index of the associated command.
*/
size_t idx;
};
/**
* Data structure for partner add requests.
*/
struct PartnerAddRequest
{
/**
* Kept in a DLL.
*/
struct PartnerAddRequest *next;
/**
* Kept in a DLL.
*/
struct PartnerAddRequest *prev;
/**
* Operation handle.
*/
struct TALER_EXCHANGE_ManagementAddPartner *h;
/**
* Array index of the associated command.
*/
size_t idx;
};
/** /**
* Next work item to perform. * Next work item to perform.
*/ */
@ -508,6 +574,27 @@ static struct GNUNET_SCHEDULER_Task *nxt;
*/ */
static struct TALER_EXCHANGE_ManagementGetKeysHandle *mgkh; static struct TALER_EXCHANGE_ManagementGetKeysHandle *mgkh;
/**
* Active AML staff change requests.
*/
static struct AmlStaffRequest *asr_head;
/**
* Active AML staff change requests.
*/
static struct AmlStaffRequest *asr_tail;
/**
* Active partner add requests.
*/
static struct PartnerAddRequest *par_head;
/**
* Active partner add requests.
*/
static struct PartnerAddRequest *par_tail;
/** /**
* Active denomiantion revocation requests. * Active denomiantion revocation requests.
*/ */
@ -629,6 +716,36 @@ do_shutdown (void *cls)
{ {
(void) cls; (void) cls;
{
struct AmlStaffRequest *asr;
while (NULL != (asr = asr_head))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Aborting incomplete AML staff update #%u\n",
(unsigned int) asr->idx);
TALER_EXCHANGE_management_update_aml_officer_cancel (asr->h);
GNUNET_CONTAINER_DLL_remove (asr_head,
asr_tail,
asr);
GNUNET_free (asr);
}
}
{
struct PartnerAddRequest *par;
while (NULL != (par = par_head))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Aborting incomplete partner add request #%u\n",
(unsigned int) par->idx);
TALER_EXCHANGE_management_add_partner_cancel (par->h);
GNUNET_CONTAINER_DLL_remove (par_head,
par_tail,
par);
GNUNET_free (par);
}
}
{ {
struct DenomRevocationRequest *drr; struct DenomRevocationRequest *drr;
@ -842,6 +959,8 @@ static void
test_shutdown (void) test_shutdown (void)
{ {
if ( (NULL == drr_head) && if ( (NULL == drr_head) &&
(NULL == par_head) &&
(NULL == asr_head) &&
(NULL == srr_head) && (NULL == srr_head) &&
(NULL == aar_head) && (NULL == aar_head) &&
(NULL == adr_head) && (NULL == adr_head) &&
@ -2214,6 +2333,221 @@ upload_extensions (const char *exchange_url,
} }
/**
* Function called with information about the add partner operation.
*
* @param cls closure with a `struct PartnerAddRequest`
* @param hr HTTP response data
*/
static void
add_partner_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
{
struct PartnerAddRequest *par = cls;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Upload failed for command %u with status %u: %s (%s)\n",
(unsigned int) par->idx,
hr->http_status,
TALER_ErrorCode_get_hint (hr->ec),
hr->hint);
global_ret = EXIT_FAILURE;
}
GNUNET_CONTAINER_DLL_remove (par_head,
par_tail,
par);
GNUNET_free (par);
test_shutdown ();
}
/**
* Add partner action.
*
* @param exchange_url base URL of the exchange
* @param idx index of the operation we are performing (for logging)
* @param value arguments for add partner
*/
static void
add_partner (const char *exchange_url,
size_t idx,
const json_t *value)
{
struct TALER_MasterPublicKeyP partner_pub;
struct GNUNET_TIME_Timestamp start_date;
struct GNUNET_TIME_Timestamp end_date;
struct GNUNET_TIME_Relative wad_frequency;
struct TALER_Amount wad_fee;
const char *partner_base_url;
struct TALER_MasterSignatureP master_sig;
struct PartnerAddRequest *par;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("partner_pub",
&partner_pub),
TALER_JSON_spec_amount ("wad_fee",
currency,
&wad_fee),
GNUNET_JSON_spec_relative_time ("wad_frequency",
&wad_frequency),
GNUNET_JSON_spec_timestamp ("start_date",
&start_date),
GNUNET_JSON_spec_timestamp ("end_date",
&end_date),
GNUNET_JSON_spec_string ("partner_base_url",
&partner_base_url),
GNUNET_JSON_spec_fixed_auto ("master_sig",
&master_sig),
GNUNET_JSON_spec_end ()
};
const char *err_name;
unsigned int err_line;
if (GNUNET_OK !=
GNUNET_JSON_parse (value,
spec,
&err_name,
&err_line))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid input to add partner: %s#%u at %u (skipping)\n",
err_name,
err_line,
(unsigned int) idx);
json_dumpf (value,
stderr,
JSON_INDENT (2));
global_ret = EXIT_FAILURE;
test_shutdown ();
return;
}
par = GNUNET_new (struct PartnerAddRequest);
par->idx = idx;
par->h =
TALER_EXCHANGE_management_add_partner (ctx,
exchange_url,
&partner_pub,
start_date,
end_date,
wad_frequency,
&wad_fee,
partner_base_url,
&master_sig,
&add_partner_cb,
par);
GNUNET_CONTAINER_DLL_insert (par_head,
par_tail,
par);
}
/**
* Function called with information about the AML officer update operation.
*
* @param cls closure with a `struct AmlStaffRequest`
* @param hr HTTP response data
*/
static void
update_aml_officer_cb (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr)
{
struct AmlStaffRequest *asr = cls;
if (MHD_HTTP_NO_CONTENT != hr->http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Upload failed for command %u with status %u: %s (%s)\n",
(unsigned int) asr->idx,
hr->http_status,
TALER_ErrorCode_get_hint (hr->ec),
hr->hint);
global_ret = EXIT_FAILURE;
}
GNUNET_CONTAINER_DLL_remove (asr_head,
asr_tail,
asr);
GNUNET_free (asr);
test_shutdown ();
}
/**
* Upload AML staff action.
*
* @param exchange_url base URL of the exchange
* @param idx index of the operation we are performing (for logging)
* @param value arguments for AML staff change
*/
static void
update_aml_staff (const char *exchange_url,
size_t idx,
const json_t *value)
{
struct TALER_AmlOfficerPublicKeyP officer_pub;
const char *officer_name;
struct GNUNET_TIME_Timestamp change_date;
bool is_active;
bool read_only;
struct TALER_MasterSignatureP master_sig;
struct AmlStaffRequest *asr;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("officer_pub",
&officer_pub),
GNUNET_JSON_spec_timestamp ("change_date",
&change_date),
GNUNET_JSON_spec_bool ("is_active",
&is_active),
GNUNET_JSON_spec_bool ("read_only",
&read_only),
GNUNET_JSON_spec_string ("officer_name",
&officer_name),
GNUNET_JSON_spec_fixed_auto ("master_sig",
&master_sig),
GNUNET_JSON_spec_end ()
};
const char *err_name;
unsigned int err_line;
if (GNUNET_OK !=
GNUNET_JSON_parse (value,
spec,
&err_name,
&err_line))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid input to AML staff update: %s#%u at %u (skipping)\n",
err_name,
err_line,
(unsigned int) idx);
json_dumpf (value,
stderr,
JSON_INDENT (2));
global_ret = EXIT_FAILURE;
test_shutdown ();
return;
}
asr = GNUNET_new (struct AmlStaffRequest);
asr->idx = idx;
asr->h =
TALER_EXCHANGE_management_update_aml_officer (ctx,
exchange_url,
&officer_pub,
officer_name,
change_date,
is_active,
read_only,
&master_sig,
&update_aml_officer_cb,
asr);
GNUNET_CONTAINER_DLL_insert (asr_head,
asr_tail,
asr);
}
/** /**
* Perform uploads based on the JSON in #out. * Perform uploads based on the JSON in #out.
* *
@ -2267,6 +2601,14 @@ trigger_upload (const char *exchange_url)
.key = OP_EXTENSIONS, .key = OP_EXTENSIONS,
.cb = &upload_extensions .cb = &upload_extensions
}, },
{
.key = OP_UPDATE_AML_STAFF,
.cb = &update_aml_staff
},
{
.key = OP_ADD_PARTNER,
.cb = &add_partner
},
/* array termination */ /* array termination */
{ {
.key = NULL .key = NULL
@ -3040,6 +3382,261 @@ do_drain (char *const *args)
} }
/**
* Add partner.
*
* @param args the array of command-line arguments to process next;
* args[0] must be the partner's master public key, args[1] the partner's
* API base URL, args[2] the wad fee, args[3] the wad frequency, and
* args[4] the year (including possibly 'now')
*/
static void
do_add_partner (char *const *args)
{
struct TALER_MasterPublicKeyP partner_pub;
struct GNUNET_TIME_Timestamp start_date;
struct GNUNET_TIME_Timestamp end_date;
struct GNUNET_TIME_Relative wad_frequency;
struct TALER_Amount wad_fee;
const char *partner_base_url;
struct TALER_MasterSignatureP master_sig;
char dummy;
unsigned int year;
if (NULL != in)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Downloaded data was not consumed, not adding partner\n");
test_shutdown ();
global_ret = EXIT_FAILURE;
return;
}
if ( (NULL == args[0]) ||
(GNUNET_OK !=
GNUNET_STRINGS_string_to_data (args[0],
strlen (args[0]),
&partner_pub,
sizeof (partner_pub))) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"You must specify the partner master public key as first argument for this subcommand\n");
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
}
if ( (NULL == args[1]) ||
(0 != strncmp ("http",
args[1],
strlen ("http"))) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"You must specify the partner's base URL as the 2nd argument to this subcommand\n");
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
}
partner_base_url = args[1];
if ( (NULL == args[2]) ||
(GNUNET_OK !=
TALER_string_to_amount (args[2],
&wad_fee)) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid amount `%s' specified for wad fee of partner\n",
args[2]);
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
}
if ( (NULL == args[3]) ||
(GNUNET_OK !=
GNUNET_STRINGS_fancy_time_to_relative (args[3],
&wad_frequency)) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid wad frequency `%s' specified for add partner\n",
args[3]);
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
}
if ( (NULL == args[4]) ||
( (1 != sscanf (args[4],
"%u%c",
&year,
&dummy)) &&
(0 != strcasecmp ("now",
args[4])) ) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid year `%s' specified for add partner\n",
args[4]);
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
}
if (0 == strcasecmp ("now",
args[4]))
year = GNUNET_TIME_get_current_year ();
start_date = GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_year_to_time (year));
end_date = GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_year_to_time (year + 1));
if (GNUNET_OK !=
load_offline_key (GNUNET_NO))
return;
TALER_exchange_offline_partner_details_sign (&partner_pub,
start_date,
end_date,
wad_frequency,
&wad_fee,
partner_base_url,
&master_priv,
&master_sig);
output_operation (OP_ADD_PARTNER,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("partner_base_url",
partner_base_url),
GNUNET_JSON_pack_time_rel ("wad_frequency",
wad_frequency),
GNUNET_JSON_pack_timestamp ("start_date",
start_date),
GNUNET_JSON_pack_timestamp ("end_date",
end_date),
GNUNET_JSON_pack_data_auto ("partner_pub",
&partner_pub),
GNUNET_JSON_pack_data_auto ("master_sig",
&master_sig)));
next (args + 5);
}
/**
* Enable or disable AML staff.
*
* @param is_active true to enable, false to disable
* @param args the array of command-line arguments to process next; args[0] must be the AML staff's public key, args[1] the AML staff's legal name, and if @a is_active then args[2] rw (read write) or ro (read only)
*/
static void
do_set_aml_staff (bool is_active,
char *const *args)
{
struct TALER_AmlOfficerPublicKeyP officer_pub;
const char *officer_name;
bool read_only;
struct TALER_MasterSignatureP master_sig;
struct GNUNET_TIME_Timestamp now
= GNUNET_TIME_timestamp_get ();
if (NULL != in)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Downloaded data was not consumed, not updating AML staff status\n");
test_shutdown ();
global_ret = EXIT_FAILURE;
return;
}
if ( (NULL == args[0]) ||
(GNUNET_OK !=
GNUNET_STRINGS_string_to_data (args[0],
strlen (args[0]),
&officer_pub,
sizeof (officer_pub))) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"You must specify the AML officer's public key as first argument for this subcommand\n");
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
}
if (NULL == args[1])
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"You must specify the officer's legal name as the 2nd argument to this subcommand\n");
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
}
officer_name = args[1];
if (is_active)
{
if ( (NULL == args[2]) ||
( (0 != strcmp (args[2],
"ro")) &&
(0 != strcmp (args[2],
"rw")) ) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"You must specify 'ro' or 'rw' (and not `%s') for the access level\n",
args[2]);
test_shutdown ();
global_ret = EXIT_INVALIDARGUMENT;
return;
}
read_only = (0 == strcmp (args[2],
"ro"));
}
else
{
read_only = true;
}
if (GNUNET_OK !=
load_offline_key (GNUNET_NO))
return;
TALER_exchange_offline_aml_officer_status_sign (&officer_pub,
officer_name,
now,
is_active,
read_only,
&master_priv,
&master_sig);
output_operation (OP_UPDATE_AML_STAFF,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("officer_name",
officer_name),
GNUNET_JSON_pack_timestamp ("change_date",
now),
GNUNET_JSON_pack_bool ("is_active",
is_active),
GNUNET_JSON_pack_bool ("read_only",
read_only),
GNUNET_JSON_pack_data_auto ("officer_pub",
&officer_pub),
GNUNET_JSON_pack_data_auto ("master_sig",
&master_sig)));
next (args + (is_active ? 3 : 2));
}
/**
* Disable AML staff.
*
* @param args the array of command-line arguments to process next;
* args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only)
*/
static void
disable_aml_staff (char *const *args)
{
do_set_aml_staff (false,
args);
}
/**
* Enable AML staff.
*
* @param args the array of command-line arguments to process next;
* args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only)
*/
static void
enable_aml_staff (char *const *args)
{
do_set_aml_staff (true,
args);
}
/** /**
* Function called with information about future keys. Dumps the JSON output * Function called with information about future keys. Dumps the JSON output
* (on success), either into an internal buffer or to stdout (depending on * (on success), either into an internal buffer or to stdout (depending on
@ -4475,6 +5072,24 @@ work (void *cls)
"drain profits from exchange escrow account to regular exchange operator account (amount, debit account configuration section and credit account payto://-URI must be given as arguments)", "drain profits from exchange escrow account to regular exchange operator account (amount, debit account configuration section and credit account payto://-URI must be given as arguments)",
.cb = &do_drain .cb = &do_drain
}, },
{
.name = "add-partner",
.help =
"add partner exchange for P2P wad transfers (partner master public key, partner base URL, wad fee, wad frequency and validity year must be given as arguments)",
.cb = &do_add_partner
},
{
.name = "aml-enable",
.help =
"enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)",
.cb = &enable_aml_staff
},
{
.name = "aml-disable",
.help =
"disable AML staff member (staff member public key and legal name must be given as arguments)",
.cb = &disable_aml_staff
},
{ {
.name = "upload", .name = "upload",
.help = .help =

View File

@ -4410,7 +4410,7 @@ TALER_EXCHANGE_management_add_partner (
/** /**
* Cancel #TALER_EXCHANGE_management_update_aml_officer() operation. * Cancel #TALER_EXCHANGE_management_add_partner() operation.
* *
* @param rh handle of the operation to cancel * @param rh handle of the operation to cancel
*/ */