From: Nick Porter Date: Wed, 19 May 2021 18:25:22 +0000 (+0100) Subject: v4: Convert %(date: ) to new xlat API (#4080) X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=17c760b1d04571a4dd67b9cb30083acde5933a16;p=thirdparty%2Ffreeradius-server.git v4: Convert %(date: ) to new xlat API (#4080) * Convert %(date: ) to new xlat API * Add tests for %(date: ) xlat * Amend docs to match new %(date: ) syntax Co-authored-by: Arran Cudbard-Bell --- diff --git a/doc/antora/modules/raddb/pages/mods-available/date.adoc b/doc/antora/modules/raddb/pages/mods-available/date.adoc index 044644c8edf..793e7debdf6 100644 --- a/doc/antora/modules/raddb/pages/mods-available/date.adoc +++ b/doc/antora/modules/raddb/pages/mods-available/date.adoc @@ -22,8 +22,8 @@ is passed. The input string can be: - - an attribute name of a date or integer attribute; - - an attribute name of a string attribute; + - an expanded date or integer attribute; + - an expanded string attribute; - the fixed strings `request` or `now`. ."Attribute" mode: @@ -74,7 +74,7 @@ The core `xlat`, `%T` returns the request timestamp in `ISO` format including milliseconds. This expansion returns it without the millisecond component. -Use e.g. `%{date_iso:request}:` +Use e.g. `%(date_iso:request):` == Default Configuration diff --git a/raddb/mods-available/date b/raddb/mods-available/date index bde53456c1d..9030460fc6e 100644 --- a/raddb/mods-available/date +++ b/raddb/mods-available/date @@ -26,8 +26,8 @@ date { # # The input string can be: # - # - an attribute name of a date or integer attribute; - # - an attribute name of a string attribute; + # - an expanded date or integer attribute; + # - an expanded string attribute; # - the fixed strings `request` or `now`. # # ."Attribute" mode: @@ -81,7 +81,7 @@ date { # including milliseconds. This expansion returns it without the # millisecond component. # -# Use e.g. `%{date_iso:request}:` +# Use e.g. `%(date_iso:request):` # date date_iso { format = "%Y-%m-%dT%H:%M:%SZ" diff --git a/src/modules/rlm_date/rlm_date.c b/src/modules/rlm_date/rlm_date.c index 4f040cf34d8..58c65cfa35b 100644 --- a/src/modules/rlm_date/rlm_date.c +++ b/src/modules/rlm_date/rlm_date.c @@ -42,15 +42,16 @@ static const CONF_PARSER module_config[] = { }; DIAG_OFF(format-nonliteral) -static ssize_t date_convert_string(request_t *request, char **out, size_t outlen, +static xlat_action_t date_convert_string(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request, const char *str, rlm_date_t const *inst) { - struct tm tminfo; - time_t date = 0; + struct tm tminfo; + time_t date = 0; + fr_value_box_t *vb; if (strptime(str, inst->fmt, &tminfo) == NULL) { REDEBUG("Failed to parse time string \"%s\" as format '%s'", str, inst->fmt); - return -1; + return XLAT_ACTION_FAIL; } if (inst->utc) { @@ -60,33 +61,55 @@ static ssize_t date_convert_string(request_t *request, char **out, size_t outlen } if (date < 0) { REDEBUG("Failed converting parsed time into unix time"); - return -1; + return XLAT_ACTION_FAIL; } - return snprintf(*out, outlen, "%" PRIu64, (uint64_t) date); + vb = fr_value_box_alloc(ctx, FR_TYPE_UINT64, NULL, false); + vb->vb_uint64 = (uint64_t) date; + fr_dcursor_append(out, vb); + return XLAT_ACTION_DONE; } -static ssize_t date_encode_strftime(char **out, size_t outlen, rlm_date_t const *inst, +static xlat_action_t date_encode_strftime(TALLOC_CTX *ctx, fr_dcursor_t *out, rlm_date_t const *inst, request_t *request, time_t date) { - struct tm tminfo; + struct tm tminfo; + char buff[64]; + fr_value_box_t *vb; if (inst->utc) { if (gmtime_r(&date, &tminfo) == NULL) { REDEBUG("Failed converting time string to gmtime: %s", fr_syserror(errno)); - return -1; + return XLAT_ACTION_FAIL; } } else { if (localtime_r(&date, &tminfo) == NULL) { REDEBUG("Failed converting time string to localtime: %s", fr_syserror(errno)); - return -1; + return XLAT_ACTION_FAIL; } } - return strftime(*out, outlen, inst->fmt, &tminfo); + if (strftime(buff, sizeof(buff), inst->fmt, &tminfo) == 0) return XLAT_ACTION_FAIL; + + vb = fr_value_box_alloc_null(ctx); + fr_value_box_strdup(ctx, vb, NULL, buff, false); + fr_dcursor_append(out, vb); + + return XLAT_ACTION_DONE; } DIAG_ON(format-nonliteral) +static int mod_xlat_instantiate(void *xlat_inst, UNUSED xlat_exp_t const *exp, void *uctx) +{ + *((void **)xlat_inst) = talloc_get_type_abort(uctx, rlm_date_t); + return 0; +} + +static xlat_arg_parser_t const xlat_date_convert_args[] = { + { .required = true, .single = true, .type = FR_TYPE_VOID }, + XLAT_ARG_PARSER_TERMINATOR +}; + /** Get or convert time and date * * Using the format in the module instance configuration, get @@ -94,60 +117,63 @@ DIAG_ON(format-nonliteral) * * When the request arrived: @verbatim -%{date:request} +%(date:request) @endverbatim * * Now: @verbatim -%{date:now} +%(date:now} @endverbatim * * Examples (Tmp-Integer-0 = 1506101100): @verbatim update request { - &Tmp-String-0 := "%{date:&Tmp-Integer-0}" ("Fri 22 Sep 18:25:00 BST 2017") - &Tmp-Integer-1 := "%{date:&Tmp-String-0}" (1506101100) + &Tmp-String-0 := "%(date:%{Tmp-Integer-0})" ("Fri 22 Sep 18:25:00 BST 2017") + &Tmp-Integer-1 := "%(date:%{Tmp-String-0})" (1506101100) } @endverbatim * * @ingroup xlat_functions */ -static ssize_t xlat_date_convert(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen, - void const *mod_inst, UNUSED void const *xlat_inst, - request_t *request, char const *fmt) +static xlat_action_t xlat_date_convert(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request, + void const *xlat_inst, UNUSED void *xlat_thread_inst, + fr_value_box_list_t *in) { - rlm_date_t const *inst = mod_inst; - struct tm tminfo; - fr_pair_t *vp; + rlm_date_t const *inst; + void *instance; + struct tm tminfo; + fr_value_box_t *arg = fr_dlist_head(in); + + memcpy(&instance, xlat_inst, sizeof(instance)); + + inst = talloc_get_type_abort(instance, rlm_date_t); memset(&tminfo, 0, sizeof(tminfo)); - if (strcmp(fmt, "request") == 0) { - return date_encode_strftime(out, outlen, inst, request, + if ((arg->type == FR_TYPE_STRING) && (strcmp(arg->vb_strvalue, "request") == 0)) { + return date_encode_strftime(ctx, out, inst, request, fr_time_to_sec(request->packet->timestamp)); } - if (strcmp(fmt, "now") == 0) { - return date_encode_strftime(out, outlen, inst, request, fr_time_to_sec(fr_time())); + if ((arg->type == FR_TYPE_STRING) && (strcmp(arg->vb_strvalue, "now") == 0)) { + return date_encode_strftime(ctx, out, inst, request, fr_time_to_sec(fr_time())); } - if ((xlat_fmt_get_vp(&vp, request, fmt) < 0) || !vp) return 0; - - switch (vp->vp_type) { + switch (arg->type) { /* * These are 'to' types, i.e. we'll convert the integers * to a time structure, and then output it in the specified * format as a string. */ case FR_TYPE_DATE: - return date_encode_strftime(out, outlen, inst, request, fr_unix_time_to_sec(vp->vp_date)); + return date_encode_strftime(ctx, out, inst, request, fr_unix_time_to_sec(arg->vb_date)); case FR_TYPE_UINT32: - return date_encode_strftime(out, outlen, inst, request, (time_t) vp->vp_uint32); + return date_encode_strftime(ctx, out, inst, request, (time_t) arg->vb_uint32); case FR_TYPE_UINT64: - return date_encode_strftime(out, outlen, inst, request, (time_t) vp->vp_uint64); + return date_encode_strftime(ctx, out, inst, request, (time_t) arg->vb_uint64); /* * These are 'from' types, i.e. we'll convert the input string @@ -155,25 +181,28 @@ static ssize_t xlat_date_convert(UNUSED TALLOC_CTX *ctx, char **out, size_t outl * unix timestamp. */ case FR_TYPE_STRING: - return date_convert_string(request, out, outlen, vp->vp_strvalue, inst); + return date_convert_string(ctx, out, request, arg->vb_strvalue, inst); default: - REDEBUG("Can't convert type %s into date", fr_table_str_by_value(fr_value_box_type_table, vp->da->type, "")); + REDEBUG("Can't convert type %s into date", fr_table_str_by_value(fr_value_box_type_table, arg->type, "")); } - return -1; + return XLAT_ACTION_FAIL; } static int mod_bootstrap(void *instance, CONF_SECTION *conf) { - rlm_date_t *inst = instance; + rlm_date_t *inst = instance; + xlat_t *xlat; inst->xlat_name = cf_section_name2(conf); if (!inst->xlat_name) { inst->xlat_name = cf_section_name1(conf); } - xlat_register_legacy(inst, inst->xlat_name, xlat_date_convert, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN); + xlat = xlat_register(inst, inst->xlat_name, xlat_date_convert, false); + xlat_func_args(xlat,xlat_date_convert_args); + xlat_async_instantiate_set(xlat, mod_xlat_instantiate, rlm_date_t *, NULL, inst); return 0; } diff --git a/src/tests/keywords/date b/src/tests/keywords/date new file mode 100644 index 00000000000..53c22b52f31 --- /dev/null +++ b/src/tests/keywords/date @@ -0,0 +1,99 @@ +# +# PRE: update if +# + +# Use pre-defined date and time +update request { + &Tmp-Integer-0 := 1506101100; +} + +# Convert to string representation +update request { + &Tmp-String-0 := "%(date:%{Tmp-Integer-0})" +} + +# Some systems report GMT some UTC... +if (&Tmp-String-0 != "Fri 22 Sep 17:25:00 GMT 2017") && (&Tmp-String-0 != "Fri 22 Sep 17:25:00 UTC 2017") { + test_fail +} + +# Convert string to integer +update request { + &Tmp-Integer-1 := "%(date:%{Tmp-String-0})" +} + +if (&Tmp-Integer-1 != &Tmp-Integer-0) { + test_fail +} + +# Compare two methods of reading request timestamp in local timezone +update request { + &Tmp-String-0 := "%(localdate:request)" + &Tmp-String-1 := "%S" +} + +if (&Tmp-String-0 != &Tmp-String-1) { + test_fail +} + +# Convert different string format +update request { + &Tmp-String-2 := "2017-09-22 17:25:00" +} + +update request { + &Tmp-Integer-2 := "%(sqldate:%{Tmp-String-2})" +} + +if (&Tmp-Integer-2 != &Tmp-Integer-0) { + test_fail +} + +# Use a date attribute +update request { + &Tmp-Date-0 := 1659985459 +} + +update request { + &Tmp-String-2 := "%(sqldate:%{Tmp-Date-0})" +} + +if (&Tmp-String-2 != '2022-08-08 19:04:19') { + test_fail +} + +# Invalid format +update request { + &Tmp-String-3 := '201-32-22 17:25:00' +} + +update request { + &Tmp-Integer-3 := "%(sqldate:%{Tmp-String-3})" +} + +if (&Tmp-Integer-3 != 0) { + test_fail +} + +if (&Module-Failure-Message != "Failed to parse time string \"201-32-22 17:25:00\" as format '\%Y-\%m-\%d \%H:\%M:\%S'") { + test_fail +} + +update request { + &NAS-IP-Address := "192.168.1.1" +} + +# Invalid type +update request { + &Tmp-String-4 := "%(date:%{NAS-IP-Address})" +} + +if (&Tmp-String-4 != "") { + test_fail +} + +if (&Module-Failure-Message != "Can't convert type ipaddr into date") { + test_fail +} + +success \ No newline at end of file diff --git a/src/tests/keywords/radius.conf b/src/tests/keywords/radius.conf index 8e7bd24c81e..bbdb5579f6b 100644 --- a/src/tests/keywords/radius.conf +++ b/src/tests/keywords/radius.conf @@ -38,11 +38,25 @@ modules { } + date { + format = "%a %d %b %H:%M:%S %Z %Y" + utc = yes + } + + date localdate { + format = "%Y-%m-%d %H:%M:%S" + utc = no + } + + date sqldate { + format = "%Y-%m-%d %H:%M:%S" + utc = yes + } + idn { allow_unassigned = no use_std3_ascii_rules = yes } - } policy {