]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
v4: Convert %(date: ) to new xlat API (#4080)
authorNick Porter <nick@portercomputing.co.uk>
Wed, 19 May 2021 18:25:22 +0000 (19:25 +0100)
committerGitHub <noreply@github.com>
Wed, 19 May 2021 18:25:22 +0000 (20:25 +0200)
* Convert %(date: ) to new xlat API

* Add tests for %(date: ) xlat

* Amend docs to match new %(date: ) syntax

Co-authored-by: Arran Cudbard-Bell <a.cudbardb@gmail.com>
doc/antora/modules/raddb/pages/mods-available/date.adoc
raddb/mods-available/date
src/modules/rlm_date/rlm_date.c
src/tests/keywords/date [new file with mode: 0644]
src/tests/keywords/radius.conf

index 044644c8edf3cd3fc6c2125542a7d1f032461124..793e7debdf66abb5a355eab2e344bdc8bab1ed29 100644 (file)
@@ -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
index bde53456c1df125ddd762bd5537f6258bf1d3186..9030460fc6e56cb92cb9f30a857fc34e1aac3c64 100644 (file)
@@ -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"
index 4f040cf34d8477982b264be4f6f4f916083b9d78..58c65cfa35bf589dedb605d33a16ba647e3e8dd2 100644 (file)
@@ -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, "<INVALID>"));
+               REDEBUG("Can't convert type %s into date", fr_table_str_by_value(fr_value_box_type_table, arg->type, "<INVALID>"));
        }
 
-       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 (file)
index 0000000..53c22b5
--- /dev/null
@@ -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
index 8e7bd24c81e71d0aa69429e18c5f25dab6c55b53..bbdb5579f6bab502b718971eb0472d1e5c91e0f5 100644 (file)
@@ -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 {