diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c index b6585ed58..bb3ca4795 100644 --- a/src/exchange/taler-exchange-httpd_common_kyc.c +++ b/src/exchange/taler-exchange-httpd_common_kyc.c @@ -19,9 +19,12 @@ * @author Christian Grothoff */ #include "platform.h" +#include "taler-exchange-httpd.h" #include "taler-exchange-httpd_common_kyc.h" #include "taler_attributes.h" +#include "taler_error_codes.h" #include "taler_exchangedb_plugin.h" +#include struct TEH_KycAmlTrigger { @@ -114,7 +117,7 @@ kyc_aml_finished (void *cls, size_t eas; void *ea; const char *birthdate; - unsigned int birthday; + unsigned int birthday = 0; struct GNUNET_ShortHashCode kyc_prox; struct GNUNET_AsyncScopeSave old_scope; @@ -125,9 +128,29 @@ kyc_aml_finished (void *cls, &kyc_prox); birthdate = json_string_value (json_object_get (kat->attributes, TALER_ATTRIBUTE_BIRTHDATE)); - birthday = 0; (void) birthdate; // FIXME-Oec: calculate birthday here... - // Convert 'birthdate' to time after 1970, then compute days. - // Then compare against max age-restriction, and if before, set to 0. + + if (TEH_age_restriction_enabled) + { + enum GNUNET_GenericReturnValue ret; + + ret = TALER_parse_coarse_date (birthdate, + &TEH_age_restriction_config.mask, + &birthday); + + if (GNUNET_OK != ret) + { + GNUNET_break (0); + if (NULL != kat->response) + MHD_destroy_response (kat->response); + kat->http_status = MHD_HTTP_BAD_REQUEST; + kat->response = TALER_MHD_make_error ( + TALER_EC_GENERIC_PARAMETER_MALFORMED, + TALER_ATTRIBUTE_BIRTHDATE); + + /* FIXME-Christian: shouldn't we return in the error case? */ + } + } + TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key, kat->attributes, &ea, @@ -159,6 +182,8 @@ kyc_aml_finished (void *cls, kat->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; kat->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, "do_insert_kyc_attributes"); + + /* FIXME-Christian: shouldn't we return in the error case? */ } /* Finally, return result to main handler */ kat->cb (kat->cb_cls, diff --git a/src/include/taler_util.h b/src/include/taler_util.h index 1f3a4c706..dc30fca31 100644 --- a/src/include/taler_util.h +++ b/src/include/taler_util.h @@ -21,6 +21,7 @@ #ifndef TALER_UTIL_H #define TALER_UTIL_H +#include #define __TALER_UTIL_LIB_H_INSIDE__ #include @@ -510,6 +511,33 @@ char *strchrnul (const char *s, int c); #endif +/** + * @brief Parses a date information into days after 1970-01-01 (or 0) + * + * The input MUST be of the form + * + * 1) YYYY-MM-DD, representing a valid date + * 2) YYYY-MM-00, representing a valid month in a particular year + * 3) YYYY-00-00, representing a valid year. + * + * In the cases 2) and 3) the out parameter is set to the beginning of the + * time, f.e. 1950-00-00 == 1950-01-01 and 1888-03-00 == 1888-03-01 + * + * The output will set to the number of days after 1970-01-01 or 0, if the input + * represents a date belonging to the largest allowed age group. + * + * @param in Input string representation of the date + * @param mask Age mask + * @param[out] out Where to write the result + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise + */ +enum GNUNET_GenericReturnValue +TALER_parse_coarse_date ( + const char *in, + const struct TALER_AgeMask *mask, + uint32_t *out); + + /** * @brief Parses a string as a list of age groups. * diff --git a/src/util/age_restriction.c b/src/util/age_restriction.c index b667fa3a9..839ed7cd5 100644 --- a/src/util/age_restriction.c +++ b/src/util/age_restriction.c @@ -710,4 +710,57 @@ TALER_age_restriction_from_secret ( } +enum GNUNET_GenericReturnValue +TALER_parse_coarse_date ( + const char *in, + const struct TALER_AgeMask *mask, + uint32_t *out) +{ + struct tm date = {0}; + struct tm limit = {0}; + time_t seconds; + + if (NULL == in) + { + /* FIXME[oec]: correct behaviour? */ + *out = 0; + return GNUNET_OK; + } + + GNUNET_assert (NULL !=mask); + GNUNET_assert (NULL !=out); + + if (NULL == strptime (in, "%Y-%0m-%0d", &date)) + { + if (NULL == strptime (in, "%Y-%0m-00", &date)) + if (NULL == strptime (in, "%Y-00-00", &date)) + return GNUNET_SYSERR; + + /* turns out that the day is off by one in the last two cases */ + date.tm_mday += 1; + } + + seconds = mktime (&date); + if (-1 == seconds) + return GNUNET_SYSERR; + + /* calculate the limit date for the largest age group */ + localtime_r (&(time_t){time (NULL)}, &limit); + limit.tm_year -= TALER_get_lowest_age (mask, 255); + GNUNET_assert (-1 != mktime (&limit)); + + if ((limit.tm_year < date.tm_year) + || ((limit.tm_year == date.tm_year) + && (limit.tm_mon < date.tm_mon)) + || ((limit.tm_year == date.tm_year) + && (limit.tm_mon == date.tm_mon) + && (limit.tm_mday < date.tm_mday))) + *out = seconds / 60 / 60 / 24; + else + *out = 0; + + return GNUNET_OK; +} + + /* end util/age_restriction.c */ diff --git a/src/util/test_age_restriction.c b/src/util/test_age_restriction.c index e1979314e..29e722acc 100644 --- a/src/util/test_age_restriction.c +++ b/src/util/test_age_restriction.c @@ -129,6 +129,77 @@ test_groups (void) } +enum GNUNET_GenericReturnValue +test_dates (void) +{ + struct TALER_AgeMask mask = { + .bits = 1 | 1 << 5 | 1 << 9 | 1 << 13 | 1 << 17 | 1 << 21 + }; + + struct + { + char *date; + uint32_t expected; + enum GNUNET_GenericReturnValue ret; + } + test [] = { + {.date = "abcd-00-00", .expected = 0, .ret = GNUNET_SYSERR}, + {.date = "1900-00-01", .expected = 0, .ret = GNUNET_SYSERR}, + {.date = "19000001", .expected = 0, .ret = GNUNET_SYSERR}, + {.date = "2001-33-05", .expected = 0, .ret = GNUNET_SYSERR}, + {.date = "2001-33-35", .expected = 0, .ret = GNUNET_SYSERR}, + + {.date = "1900-00-00", .expected = 0, .ret = GNUNET_OK}, + {.date = "2001-00-00", .expected = 0, .ret = GNUNET_OK}, + {.date = "2001-03-00", .expected = 0, .ret = GNUNET_OK}, + {.date = "2001-03-05", .expected = 0, .ret = GNUNET_OK}, + + /* These dates should be far enough for the near future so that + * the expected values are correct. Will need adjustment in 2044 :) */ + {.date = "2023-06-26", .expected = 19533, .ret = GNUNET_OK }, + {.date = "2023-06-01", .expected = 19508, .ret = GNUNET_OK }, + {.date = "2023-06-00", .expected = 19508, .ret = GNUNET_OK }, + {.date = "2023-01-01", .expected = 19357, .ret = GNUNET_OK }, + {.date = "2023-00-00", .expected = 19357, .ret = GNUNET_OK }, + }; + + for (uint8_t t = 0; t < sizeof(test) / sizeof(test[0]); t++) + { + uint32_t d; + enum GNUNET_GenericReturnValue ret; + + ret = TALER_parse_coarse_date (test[t].date, + &mask, + &d); + if (ret != test[t].ret) + { + printf ( + "dates[%d] for date `%s` expected parser to return: %d, got: %d\n", + t, test[t].date, test[t].ret, ret); + return GNUNET_SYSERR; + } + + if (ret == GNUNET_SYSERR) + continue; + + if (d != test[t].expected) + { + printf ( + "dates[%d] for date `%s` expected value %d, but got %d\n", + t, test[t].date, test[t].expected, d); + return GNUNET_SYSERR; + } + + printf ("dates[%d] for date `%s` got expected value %d\n", + t, test[t].date, d); + } + + printf ("done with dates\n"); + + return GNUNET_OK; +} + + enum GNUNET_GenericReturnValue test_lowest (void) { @@ -308,6 +379,8 @@ main (int argc, GNUNET_break (0); return 3; } + if (GNUNET_OK != test_dates ()) + return 4; return 0; }