add persona attribute conversion logic
This commit is contained in:
parent
85e44ceea6
commit
0eb6f73176
@ -61,12 +61,31 @@ extern "C" {
|
||||
#define TALER_ATTRIBUTE_PEP "pep"
|
||||
|
||||
/**
|
||||
* Phone number (of business or individual).
|
||||
* Street-level address. Usually includes the street and the house number. May
|
||||
* consist of multiple lines (separated by '\n'). Identifies a house in a city. The city is not
|
||||
* part of the street.
|
||||
*/
|
||||
#define TALER_ATTRIBUTE_ADDRESS_STREET "street"
|
||||
|
||||
/**
|
||||
* City including postal code. If available, a 2-letter country-code prefixes
|
||||
* the postal code, which is before the city (e.g. "DE-42289 Wuppertal"). If
|
||||
* the country code is unknown, the "CC-" prefix is missing. If the ZIP code
|
||||
* is unknown, the hyphen is followed by a space ("DE- Wuppertal"). If only
|
||||
* the city name is known, it is prefixed by a space (" ").
|
||||
* If the city name is unknown, a space is at the end of the value.
|
||||
*/
|
||||
#define TALER_ATTRIBUTE_ADDRESS_CITY "city"
|
||||
|
||||
/**
|
||||
* Phone number (of business or individual). Should come with the "+CC"
|
||||
* prefix including the country code.
|
||||
*/
|
||||
#define TALER_ATTRIBUTE_PHONE "phone"
|
||||
|
||||
/**
|
||||
* Email address (of business or individual).
|
||||
* Email address (of business or individual). Should be
|
||||
* in the format "user@hostname".
|
||||
*/
|
||||
#define TALER_ATTRIBUTE_EMAIL "email"
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
Copyright (C) 2022 Taler Systems SA
|
||||
Copyright (C) 2022, 2023 Taler Systems SA
|
||||
|
||||
Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU Affero General Public License as published by the Free Software
|
||||
@ -19,6 +19,7 @@
|
||||
* @author Christian Grothoff
|
||||
*/
|
||||
#include "platform.h"
|
||||
#include "taler_attributes.h"
|
||||
#include "taler_kyclogic_plugin.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
#include "taler_curl_lib.h"
|
||||
@ -62,8 +63,9 @@ struct PluginState
|
||||
struct GNUNET_CURL_RescheduleContext *curl_rc;
|
||||
|
||||
/**
|
||||
* Authorization token to use when receiving webhooks from the Persona service. Optional. Note that
|
||||
* webhooks are *global* and not per template.
|
||||
* Authorization token to use when receiving webhooks from the Persona
|
||||
* service. Optional. Note that webhooks are *global* and not per
|
||||
* template.
|
||||
*/
|
||||
char *webhook_token;
|
||||
|
||||
@ -923,9 +925,229 @@ proof_reply_error (struct TALER_KYCLOGIC_ProofHandle *ph,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert KYC attribute data from Persona response.
|
||||
*
|
||||
* @param attr json array with Persona attribute data
|
||||
* @return KYC attribute data
|
||||
*/
|
||||
static json_t *
|
||||
convert_attributes (const json_t *attr)
|
||||
{
|
||||
const char *country_code = NULL;
|
||||
const char *name_first = NULL;
|
||||
const char *name_middle = NULL;
|
||||
const char *name_last = NULL;
|
||||
const char *address_street_1 = NULL;
|
||||
const char *address_street_2 = NULL;
|
||||
const char *address_city = NULL;
|
||||
const char *address_postal_code = NULL;
|
||||
const char *birthdate = NULL;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("country_code",
|
||||
&country_code),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("name_first",
|
||||
&name_first),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("name_middle",
|
||||
&name_middle),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("name_last",
|
||||
&name_last),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("address_street_1",
|
||||
&address_street_1),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("address_street_2",
|
||||
&address_street_2),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("address_city",
|
||||
&address_city),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("address_postal_code",
|
||||
&address_postal_code),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("birthdate",
|
||||
&birthdate),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
json_t *ret;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (attr,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return NULL;
|
||||
}
|
||||
{
|
||||
char *name = NULL;
|
||||
char *address_street = NULL;
|
||||
char *address_city = NULL;
|
||||
|
||||
if ( (NULL != name_last) ||
|
||||
(NULL != name_first) ||
|
||||
(NULL != name_middle) )
|
||||
{
|
||||
GNUNET_asprintf (&name,
|
||||
"%s, %s %s",
|
||||
(NULL != name_last)
|
||||
? name_last
|
||||
: "",
|
||||
(NULL != name_first)
|
||||
? name_first
|
||||
: "",
|
||||
(NULL != name_middle)
|
||||
? name_middle
|
||||
: "");
|
||||
}
|
||||
if ( (NULL != address_city) ||
|
||||
(NULL != address_postal_code) )
|
||||
{
|
||||
GNUNET_asprintf (&address_city,
|
||||
"%s%s%s %s",
|
||||
(NULL != country_code)
|
||||
? country_code
|
||||
: "",
|
||||
(NULL != country_code)
|
||||
? "-"
|
||||
: "",
|
||||
(NULL != address_postal_code)
|
||||
? address_postal_code
|
||||
: "",
|
||||
(NULL != address_city)
|
||||
? address_city
|
||||
: "");
|
||||
}
|
||||
if ( (NULL != address_street_1) ||
|
||||
(NULL != address_street_2) )
|
||||
{
|
||||
GNUNET_asprintf (&address_street,
|
||||
"%s%s%s",
|
||||
(NULL != address_street_1)
|
||||
? address_street_1
|
||||
: "",
|
||||
( (NULL != address_street_1) &&
|
||||
(NULL != address_street_2) )
|
||||
? "\n"
|
||||
: "",
|
||||
(NULL != address_street_2)
|
||||
? address_street_2
|
||||
: "");
|
||||
}
|
||||
ret = GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_BIRTHDATE,
|
||||
birthdate)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_FULL_NAME,
|
||||
name)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_ADDRESS_STREET,
|
||||
address_street)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_ADDRESS_CITY,
|
||||
address_city)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_RESIDENCES,
|
||||
country_code))
|
||||
);
|
||||
GNUNET_free (name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract and convert KYC attribute data from
|
||||
* Persona response.
|
||||
*
|
||||
* @param included json array with various data
|
||||
* @return KYC attribute data
|
||||
*/
|
||||
static json_t *
|
||||
extract_attributes (const json_t *included)
|
||||
{
|
||||
size_t idx;
|
||||
json_t *obj;
|
||||
|
||||
json_array_foreach (included, idx, obj)
|
||||
{
|
||||
const char *type = json_string_value (json_object_get (obj,
|
||||
"type"));
|
||||
json_t *attr;
|
||||
if (0 != strcmp (type,
|
||||
"verification/database"))
|
||||
continue;
|
||||
attr = json_object_get (obj,
|
||||
"attributes");
|
||||
return convert_attributes (attr);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a response for the @a ph request indicating a
|
||||
* protocol violation by the Persona server.
|
||||
*
|
||||
* @param[in,out] ph request we are processing
|
||||
* @param response_code HTTP status returned by Persona
|
||||
* @param inquiry_id ID of the inquiry this is about
|
||||
* @param detail where the response was wrong
|
||||
* @param data full response data to output
|
||||
*/
|
||||
static void
|
||||
return_invalid_response (struct TALER_KYCLOGIC_ProofHandle *ph,
|
||||
unsigned int response_code,
|
||||
const char *inquiry_id,
|
||||
const char *detail,
|
||||
const json_t *data)
|
||||
{
|
||||
json_dumpf (data,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
proof_reply_error (
|
||||
ph,
|
||||
inquiry_id,
|
||||
MHD_HTTP_BAD_GATEWAY,
|
||||
"persona-invalid-response",
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_uint64 ("persona_http_status",
|
||||
response_code),
|
||||
GNUNET_JSON_pack_string ("persona_inquiry_id",
|
||||
inquiry_id),
|
||||
TALER_JSON_pack_ec (
|
||||
TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY),
|
||||
GNUNET_JSON_pack_string ("detail",
|
||||
detail),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_object_incref ("data",
|
||||
(json_t *)
|
||||
data))));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called when we're done processing the
|
||||
* HTTP "/api/v1/verifications/{verification-id}" request.
|
||||
* HTTP "/api/v1/inquiries/{inquiry-id}" request.
|
||||
*
|
||||
* @param cls the `struct TALER_KYCLOGIC_InitiateHandle`
|
||||
* @param response_code HTTP response code, 0 on error
|
||||
@ -950,6 +1172,8 @@ handle_proof_finished (void *cls,
|
||||
const char *account_id;
|
||||
const char *type = NULL;
|
||||
json_t *attributes;
|
||||
json_t *relationships;
|
||||
json_t *included;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_string ("type",
|
||||
&type),
|
||||
@ -957,6 +1181,10 @@ handle_proof_finished (void *cls,
|
||||
&inquiry_id),
|
||||
GNUNET_JSON_spec_json ("attributes",
|
||||
&attributes),
|
||||
GNUNET_JSON_spec_json ("relationships",
|
||||
&relationships),
|
||||
GNUNET_JSON_spec_json ("included",
|
||||
&included),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
@ -969,25 +1197,12 @@ handle_proof_finished (void *cls,
|
||||
"inquiry")) )
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
json_dumpf (j,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
proof_reply_error (ph,
|
||||
return_invalid_response (ph,
|
||||
response_code,
|
||||
inquiry_id,
|
||||
MHD_HTTP_BAD_GATEWAY,
|
||||
"persona-logic-failure",
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_uint64 ("persona_http_status",
|
||||
response_code),
|
||||
GNUNET_JSON_pack_string ("persona_inquiry_id",
|
||||
inquiry_id),
|
||||
TALER_JSON_pack_ec (
|
||||
TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY),
|
||||
GNUNET_JSON_pack_string ("detail",
|
||||
"data"),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_object_incref ("data",
|
||||
(json_t *) data))));
|
||||
"data",
|
||||
data);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1013,25 +1228,11 @@ handle_proof_finished (void *cls,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
json_dumpf (j,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
proof_reply_error (ph,
|
||||
return_invalid_response (ph,
|
||||
response_code,
|
||||
inquiry_id,
|
||||
MHD_HTTP_BAD_GATEWAY,
|
||||
"persona-invalid-response",
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_uint64 ("persona_http_status",
|
||||
response_code),
|
||||
GNUNET_JSON_pack_string ("persona_inquiry_id",
|
||||
inquiry_id),
|
||||
TALER_JSON_pack_ec (
|
||||
TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY),
|
||||
GNUNET_JSON_pack_string ("detail",
|
||||
"data-attributes"),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_object_incref ("data",
|
||||
(json_t *) data))));
|
||||
"data-attributes",
|
||||
data);
|
||||
GNUNET_JSON_parse_free (ispec);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
break;
|
||||
@ -1047,23 +1248,11 @@ handle_proof_finished (void *cls,
|
||||
(idr != ph->process_row) )
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
proof_reply_error (ph,
|
||||
return_invalid_response (ph,
|
||||
response_code,
|
||||
inquiry_id,
|
||||
MHD_HTTP_BAD_GATEWAY,
|
||||
"persona-invalid-response",
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_uint64 ("persona_http_status",
|
||||
response_code),
|
||||
GNUNET_JSON_pack_string ("persona_inquiry_id",
|
||||
inquiry_id),
|
||||
TALER_JSON_pack_ec (
|
||||
TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY),
|
||||
GNUNET_JSON_pack_string ("detail",
|
||||
"data-attributes-reference_id"),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_object_incref ("data",
|
||||
(json_t *)
|
||||
data))));
|
||||
"data-attributes-reference_id",
|
||||
data);
|
||||
GNUNET_JSON_parse_free (ispec);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
break;
|
||||
@ -1074,23 +1263,11 @@ handle_proof_finished (void *cls,
|
||||
ph->inquiry_id))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
proof_reply_error (ph,
|
||||
return_invalid_response (ph,
|
||||
response_code,
|
||||
inquiry_id,
|
||||
MHD_HTTP_BAD_GATEWAY,
|
||||
"persona-invalid-response",
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_uint64 ("persona_http_status",
|
||||
response_code),
|
||||
GNUNET_JSON_pack_string ("persona_inquiry_id",
|
||||
inquiry_id),
|
||||
TALER_JSON_pack_ec (
|
||||
TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY),
|
||||
GNUNET_JSON_pack_string ("detail",
|
||||
"data-id"),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_object_incref ("data",
|
||||
(json_t *)
|
||||
data))));
|
||||
"data-id",
|
||||
data);
|
||||
GNUNET_JSON_parse_free (ispec);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
break;
|
||||
@ -1100,9 +1277,7 @@ handle_proof_finished (void *cls,
|
||||
json_object_get (
|
||||
json_object_get (
|
||||
json_object_get (
|
||||
json_object_get (
|
||||
data,
|
||||
"relationships"),
|
||||
relationships,
|
||||
"account"),
|
||||
"data"),
|
||||
"id"));
|
||||
@ -1110,7 +1285,8 @@ handle_proof_finished (void *cls,
|
||||
if (0 != strcmp (status,
|
||||
"completed"))
|
||||
{
|
||||
proof_generic_reply (ph,
|
||||
proof_generic_reply (
|
||||
ph,
|
||||
TALER_KYCLOGIC_STATUS_FAILED,
|
||||
account_id,
|
||||
inquiry_id,
|
||||
@ -1133,33 +1309,30 @@ handle_proof_finished (void *cls,
|
||||
if (NULL == account_id)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
json_dumpf (data,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
proof_reply_error (ph,
|
||||
return_invalid_response (ph,
|
||||
response_code,
|
||||
inquiry_id,
|
||||
MHD_HTTP_BAD_GATEWAY,
|
||||
"persona-invalid-response",
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_uint64 ("persona_http_status",
|
||||
response_code),
|
||||
GNUNET_JSON_pack_string ("persona_inquiry_id",
|
||||
inquiry_id),
|
||||
TALER_JSON_pack_ec (
|
||||
TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY),
|
||||
GNUNET_JSON_pack_string ("detail",
|
||||
"data-relationships-account-data-id"),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_object_incref ("data",
|
||||
(json_t *)
|
||||
data))));
|
||||
"data-relationships-account-data-id",
|
||||
data);
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
struct MHD_Response *resp;
|
||||
struct GNUNET_TIME_Absolute expiration;
|
||||
json_t *attr;
|
||||
|
||||
attr = extract_attributes (included);
|
||||
if (NULL == attr)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return_invalid_response (ph,
|
||||
response_code,
|
||||
inquiry_id,
|
||||
"data-relationships-account-data-id",
|
||||
data);
|
||||
break;
|
||||
}
|
||||
expiration = GNUNET_TIME_relative_to_absolute (ph->pd->validity);
|
||||
resp = MHD_create_response_from_buffer (0,
|
||||
"",
|
||||
@ -1174,9 +1347,10 @@ handle_proof_finished (void *cls,
|
||||
account_id,
|
||||
inquiry_id,
|
||||
expiration,
|
||||
NULL, /* FIXME: return attributes! */
|
||||
attr,
|
||||
MHD_HTTP_SEE_OTHER,
|
||||
resp);
|
||||
json_decref (attr);
|
||||
}
|
||||
GNUNET_JSON_parse_free (ispec);
|
||||
}
|
||||
@ -1194,7 +1368,8 @@ handle_proof_finished (void *cls,
|
||||
json_dumpf (j,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
proof_reply_error (ph,
|
||||
proof_reply_error (
|
||||
ph,
|
||||
ph->inquiry_id,
|
||||
MHD_HTTP_BAD_GATEWAY,
|
||||
"persona-logic-failure",
|
||||
@ -1214,7 +1389,8 @@ handle_proof_finished (void *cls,
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Refused access with HTTP status code %u\n",
|
||||
(unsigned int) response_code);
|
||||
proof_reply_error (ph,
|
||||
proof_reply_error (
|
||||
ph,
|
||||
ph->inquiry_id,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
"persona-exchange-unauthorized",
|
||||
@ -1233,8 +1409,8 @@ handle_proof_finished (void *cls,
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Refused access with HTTP status code %u\n",
|
||||
(unsigned int) response_code);
|
||||
|
||||
proof_reply_error (ph,
|
||||
proof_reply_error (
|
||||
ph,
|
||||
ph->inquiry_id,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
"persona-exchange-unpaid",
|
||||
@ -1256,7 +1432,8 @@ handle_proof_finished (void *cls,
|
||||
json_dumpf (j,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
proof_reply_error (ph,
|
||||
proof_reply_error (
|
||||
ph,
|
||||
ph->inquiry_id,
|
||||
MHD_HTTP_GATEWAY_TIMEOUT,
|
||||
"persona-network-timeout",
|
||||
@ -1278,7 +1455,8 @@ handle_proof_finished (void *cls,
|
||||
json_dumpf (j,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
proof_reply_error (ph,
|
||||
proof_reply_error (
|
||||
ph,
|
||||
ph->inquiry_id,
|
||||
MHD_HTTP_SERVICE_UNAVAILABLE,
|
||||
"persona-load-failure",
|
||||
@ -1300,7 +1478,8 @@ handle_proof_finished (void *cls,
|
||||
json_dumpf (j,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
proof_reply_error (ph,
|
||||
proof_reply_error (
|
||||
ph,
|
||||
ph->inquiry_id,
|
||||
MHD_HTTP_BAD_GATEWAY,
|
||||
"persona-provider-failure",
|
||||
@ -1322,7 +1501,8 @@ handle_proof_finished (void *cls,
|
||||
json_dumpf (j,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
proof_reply_error (ph,
|
||||
proof_reply_error (
|
||||
ph,
|
||||
ph->inquiry_id,
|
||||
MHD_HTTP_BAD_GATEWAY,
|
||||
"persona-invalid-response",
|
||||
|
Loading…
Reference in New Issue
Block a user