]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
v4: Convert %(perl: ) to new xlat API (#4092)
authorNick Porter <nick@portercomputing.co.uk>
Thu, 3 Jun 2021 17:13:21 +0000 (18:13 +0100)
committerGitHub <noreply@github.com>
Thu, 3 Jun 2021 17:13:21 +0000 (12:13 -0500)
* Define perl_vblist_to_av() to convert a value box list to a perl array

* Define perl_sv_to_vblist() for parsing perl values to value boxes

* Convert %(perl: ) to new xlat API

* Add simple test for %(perl: )

* Add tests for %(perl: ) passing arrays and returning arrays / hashes

* Correct perl calls to FR logging in example

* Improve perl xlat comments

* Add perl tests which access and manipulate attributes

* Improve documentation for rlm_perl

doc/antora/modules/raddb/pages/mods-available/perl.adoc
raddb/mods-available/perl
raddb/mods-config/perl/example.pl
src/modules/rlm_perl/rlm_perl.c
src/tests/keywords/perl [new file with mode: 0644]
src/tests/keywords/perl.conf [new file with mode: 0644]
src/tests/keywords/radius.conf
src/tests/modules/perl/auth.attrs [new file with mode: 0644]
src/tests/modules/perl/auth.unlang [new file with mode: 0644]
src/tests/modules/perl/module.conf
src/tests/modules/perl/test.pl

index a91020d15baa2e595027f11d12ca62984b7accd0..d6b2cb9ff60af191be9ba7665cd22fac160f60bf 100644 (file)
@@ -15,7 +15,6 @@ included in your module. If the module is called for a section which
 does not have a function defined, it will return `noop`.
 
 
-
 ## Configuration Settings
 
 The following hashes are given to the module and
@@ -97,7 +96,7 @@ For instance:
 [source,perl]
 ----
 $RAD_PERLCONF{'name'}
-$RAD_PERLCONF{'sub-config'}->{'name'}`
+$RAD_PERLCONF{'sub-config'}->{'name'}
 ----
 
 
@@ -114,7 +113,6 @@ perl {
 #      func_pre_proxy = pre_proxy
 #      func_post_proxy = post_proxy
 #      func_post_auth = post_auth
-#      func_xlat = xlat
 #      func_detach = detach
 #      func_start_accounting = accounting_start
 #      func_stop_accounting = accounting_stop
@@ -126,3 +124,29 @@ perl {
 #      }
 }
 ```
+
+## xlat for calling arbitrary Perl subroutines
+
+An xlat based on the instance name can be called to run arbitrary subroutines
+declared in the perl file configured in the instance.  The xlat takes the
+subroutine name as its first argument and then any subsequent arguments are
+passed as string scalars to the subroutine.  If any of the arguments are
+lists with multiple values e.g. `%{User-Name[*]}` where there are multiple
+instances of `User-Name` then those arguments will be passed as references
+to arrays containing all the values from the list.
+
+The return value of the subroutine is passed back as the expanded value of
+the xlat.  Since the subroutine is called in array context, all returned values
+are received, though currently they are concatenated to form a single value.
+This is due to change in the future.
+
+NOTE: The Perl xlat does not have access to the attribute lists, nor will
+setting the hashes update the attribute lists.
+
+.Example
+
+```
+update control {
+        &Tmp-String-0 := "%(perl:myfunc arg1 arg2)"
+}
+```
index f54207cae36bdd212ab889f0b220bda55df0a9db..9efba7500b15459ea9dfeb73afb1c95a19937201 100644 (file)
@@ -88,7 +88,6 @@ perl {
 #      func_pre_proxy = pre_proxy
 #      func_post_proxy = post_proxy
 #      func_post_auth = post_auth
-#      func_xlat = xlat
 #      func_detach = detach
 
        #
@@ -114,7 +113,7 @@ perl {
        #  [source,perl]
        #  ----
        #  $RAD_PERLCONF{'name'}
-       #  $RAD_PERLCONF{'sub-config'}->{'name'}`
+       #  $RAD_PERLCONF{'sub-config'}->{'name'}
        #  ----
        #
 #      config {
index d712e5e1c51d3a8caa23d22b0ed04f0606d02268..7b9b3a79dd320546ad30903bfed814ed41b05019 100644 (file)
@@ -180,14 +180,27 @@ sub post_auth {
 }
 
 # Function to handle xlat
+# Receives arguments presented to the xlat as discrete
+# arguments to the function.
+# If any of the arguments are lists with multiple values e.g.
+# %{User-Name[*]} where there are multiple instances of User-Name
+# then these will be passed as references to arrays.
+#
+# Any return value is presented as the result of the xlat
+#
+# The function is called in array context so all returned values
+# are received, though currently are concatenated before being
+# presented as the expansion of the xlat.  This is due to change
+# in the future.
+#
+# Note: Hashes for the attribute lists are not available in
+#       xlat evaluation and neither will setting them result
+#       in attributes being created.
 sub xlat {
-       # For debugging purposes only
-#      log_request_attributes();
-
        # Loads some external perl and evaluate it
        my ($filename,$a,$b,$c,$d) = @_;
-       radiusd::radlog(L_DBG, "From xlat $filename");
-       radiusd::radlog(L_DBG,"From xlat $a $b $c $d");
+       radiusd::log(L_DBG, "From xlat $filename");
+       radiusd::log(L_DBG,"From xlat $a $b $c $d");
        open(my $FH, '<', $filename) or die "open '$filename' $!";
        local($/) = undef;
        my $sub = <$FH>;
@@ -215,7 +228,7 @@ sub log_request_attributes {
        # This shouldn't be done in production environments!
        # This is only meant for debugging!
        for (keys %RAD_REQUEST) {
-               radiusd::radlog(L_DBG, "RAD_REQUEST: $_ = $RAD_REQUEST{$_}");
+               radiusd::log(L_DBG, "RAD_REQUEST: $_ = $RAD_REQUEST{$_}");
        }
 }
 
index 74dbe9f4c6bbe7941120aca6544ebad6dd133e8a..e80d806a0c16dbce2dc01ccac943b3c6ba72e5ee 100644 (file)
@@ -63,7 +63,6 @@ typedef struct {
        char const      *func_stop_accounting;
        char const      *func_preacct;
        char const      *func_detach;
-       char const      *func_xlat;
        char const      *func_post_auth;
        char const      *xlat_name;
        char const      *perl_flags;
@@ -79,6 +78,10 @@ typedef struct {
 
 } rlm_perl_t;
 
+typedef struct {
+       rlm_perl_t      *inst;          //!< Module instance
+} rlm_perl_xlat_t;
+
 static void *perl_dlhandle;            //!< To allow us to load perl's symbols into the global symbol table.
 
 /*
@@ -96,7 +99,6 @@ static const CONF_PARSER module_config[] = {
        RLM_PERL_CONF(accounting),
        RLM_PERL_CONF(preacct),
        RLM_PERL_CONF(detach),
-       RLM_PERL_CONF(xlat),
 
        { FR_CONF_OFFSET("perl_flags", FR_TYPE_STRING, rlm_perl_t, perl_flags) },
 
@@ -351,23 +353,213 @@ static void xs_init(pTHX)
        newXS("radiusd::xlat",XS_radiusd_xlat, "rlm_perl");
 }
 
+/** Convert a list of value boxes to a Perl array for passing to subroutines
+ *
+ * The Perl array object should be created before calling this
+ * to populate it.
+ *
+ * @param[in,out] av   Perl array object to append values to.
+ * @param[in] head     of VB list.
+ * @return
+ *     - 0 on success
+ *     - -1 on failure
+ */
+static int perl_vblist_to_av(AV *av, fr_value_box_list_t *head) {
+       fr_value_box_t  *vb = NULL;
+       SV              *sv;
+
+       while ((vb = fr_dlist_next(head, vb))) {
+               switch (vb->type) {
+               case FR_TYPE_STRING:
+                       sv = newSVpvn(vb->vb_strvalue, vb->length);
+                       break;
+
+               case FR_TYPE_OCTETS:
+                       sv = newSVpvn((char const *)vb->vb_octets, vb->vb_length);
+                       break;
+
+               case FR_TYPE_GROUP:
+               {
+                       AV      *sub_av;
+                       sub_av = newAV();
+                       perl_vblist_to_av(sub_av, &vb->vb_group);
+                       sv = newRV_inc((SV *)sub_av);
+               }
+                       break;
+               default:
+               {
+                       char    buffer[1024];
+                       ssize_t slen;
+
+                       slen = fr_value_box_print_quoted(&FR_SBUFF_OUT(buffer, sizeof(buffer)), vb, T_BARE_WORD);
+                       if (slen < 0) return -1;
+                       sv = newSVpvn(buffer, (size_t)slen);
+               }
+                       break;
+               }
+               if (!sv) return -1;
+               if (vb->tainted) SvTAINT(sv);
+               av_push(av, sv);
+       }
+       return 0;
+}
+
+/** Parse a Perl SV and create value boxes, appending to a list
+ *
+ * For parsing values passed back from a Perl subroutine
+ *
+ * When hashes are returned, first the key is added as a value box then the value
+ *
+ * @param[in] ctx      to allocate boxes in.
+ * @param[out] list    to append value boxes to.
+ * @param[in] request  being handled - only used for debug messages
+ * @param[in] sv       to parse
+ * @return
+ *     - 0 on success
+ *     - -1 on failure
+ */
+static int perl_sv_to_vblist(TALLOC_CTX *ctx, fr_value_box_list_t *list, request_t *request, SV *sv) {
+       fr_value_box_t  *vb = NULL;
+       char            *tmp;
+       STRLEN          len;
+       AV              *av;
+       HV              *hv;
+       I32             sv_len, i;
+       int             type;
+
+       type = SvTYPE(sv);
+
+       switch (type) {
+       case SVt_IV:
+       /*      Integer or Reference */
+               if (SvROK(sv)) {
+                       DEBUG3("Reference returned");
+                       if (perl_sv_to_vblist(ctx, list, request, SvRV(sv)) < 0) return -1;
+                       break;
+               }
+               DEBUG3("Integer returned");
+               MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_INT32, NULL, SvTAINTED(sv)));
+               vb->vb_int32 = SvIV(sv);
+               break;
+
+       case SVt_NV:
+       /*      Float */
+               DEBUG3("Float returned");
+               MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_FLOAT64, NULL, SvTAINTED(sv)));
+               vb->vb_float64 = SvNV(sv);
+               break;
+
+       case SVt_PV:
+       /*      String */
+               DEBUG3("String returned");
+               tmp = SvPVutf8(sv, len);
+               MEM(vb = fr_value_box_alloc_null(ctx));
+               if (fr_value_box_bstrndup(ctx, vb, NULL, tmp, len, SvTAINTED(sv)) < 0) {
+                       talloc_free(vb);
+                       RPEDEBUG("Failed to allocate %ld for output", len);
+                       return -1;
+               }
+               break;
+
+       case SVt_PVAV:
+       /*      Array */
+       {
+               SV      **av_sv;
+               DEBUG3("Array returned");
+               av = (AV*)sv;
+               sv_len = av_len(av);
+               for (i = 0; i <= sv_len; i++) {
+                       av_sv = av_fetch(av, i, 0);
+                       if (SvOK(*av_sv)) {
+                               if (perl_sv_to_vblist(ctx, list, request, *av_sv) < 0) return -1;
+                       }
+               }
+       }
+               break;
+
+       case SVt_PVHV:
+       /*      Hash */
+       {
+               SV      *hv_sv;
+               DEBUG3("Hash returned");
+               hv = (HV*)sv;
+               for (i = hv_iterinit(hv); i > 0; i--) {
+                       hv_sv = hv_iternextsv(hv, &tmp, &sv_len);
+                       /*
+                        *      Add key first
+                        */
+                       MEM(vb = fr_value_box_alloc_null(ctx));
+                       if (fr_value_box_bstrndup(ctx, vb, NULL, tmp, sv_len, SvTAINTED(hv_sv)) < 0) {
+                               talloc_free(vb);
+                               RPEDEBUG("Failed to allocate %d for output", sv_len);
+                               return -1;
+                       }
+                       fr_dlist_insert_tail(list, vb);
+
+                       /*
+                        *      Now process value
+                        */
+                       if (perl_sv_to_vblist(ctx, list, request, hv_sv) < 0) return -1;
+
+               }
+               /*
+                *      Box has already been added to list - return
+                */
+               return 0;
+       }
+
+       case SVt_NULL:
+               break;
+
+       default:
+               RPEDEBUG("Perl returned unsupported data type %d", type);
+               return -1;
+
+       }
+
+       if (vb) fr_dlist_insert_tail(list, vb);
+
+       return 0;
+}
+
+static int mod_xlat_instantiate(void *xlat_inst, UNUSED xlat_exp_t const *exp, void *uctx)
+{
+       rlm_perl_t      *inst = talloc_get_type_abort(uctx, rlm_perl_t);
+       rlm_perl_xlat_t *xi = talloc_get_type_abort(xlat_inst, rlm_perl_xlat_t);
+
+       xi->inst = inst;
+
+       return 0;
+}
+
+static xlat_arg_parser_t const perl_xlat_args[] = {
+       { .required = true, .single = true, .type = FR_TYPE_STRING },
+       { .variadic = true, .type = FR_TYPE_VOID },
+       XLAT_ARG_PARSER_TERMINATOR
+};
+
 /** Call perl code using an xlat
  *
  * @ingroup xlat_functions
  */
-static ssize_t perl_xlat(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 perl_xlat(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_perl_xlat_t const   *xi = talloc_get_type_abort(UNCONST(void *, xlat_inst), rlm_perl_xlat_t);
+       rlm_perl_t              *inst = xi->inst;
+       int                     count, i;
+       xlat_action_t           ret = XLAT_ACTION_FAIL;
+       STRLEN                  n_a;
+       fr_value_box_t          *func = fr_dlist_pop_head(in);
+       fr_value_box_t          *arg = NULL, *child;
+       SV                      *sv;
+       AV                      *av;
+       fr_value_box_list_t     list, sub_list;
+       fr_value_box_t          *vb = NULL;
 
-       rlm_perl_t      *inst;
-       char            *tmp;
-       char const      *p, *q;
-       int             count;
-       size_t          ret = 0;
-       STRLEN          n_a;
-
-       memcpy(&inst, &mod_inst, sizeof(inst));
+       fr_value_box_list_init(&list);
+       fr_value_box_list_init(&sub_list);
 
 #ifdef USE_ITHREADS
        PerlInterpreter *interp;
@@ -388,48 +580,64 @@ static ssize_t perl_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
 
                PUSHMARK(SP);
 
-               p = q = fmt;
-               while (*p == ' ') {
-                       p++;
-                       q++;
-               }
-               while (*q) {
-                       if (*q == ' ') {
-                               XPUSHs(sv_2mortal(newSVpvn(p, q - p)));
-                               p = q + 1;
+               while ((arg = fr_dlist_next(in, arg))) {
+                       fr_assert(arg->type == FR_TYPE_GROUP);
+                       if (fr_dlist_empty(&arg->vb_group)) continue;
 
+                       if (fr_dlist_num_elements(&arg->vb_group) == 1) {
+                               child = fr_dlist_head(&arg->vb_group);
                                /*
-                                *      Don't use an empty string
+                                *      Single child value - add as scalar
                                 */
-                               while (*p == ' ') p++;
-                               q = p;
+                               if (child->length == 0) continue;
+                               DEBUG3("Passing single value %pV", child);
+                               sv = newSVpvn(child->vb_strvalue, child->length);
+                               if (child->tainted) SvTAINT(sv);
+                               XPUSHs(sv_2mortal(sv));
+                               continue;
                        }
-                       q++;
-               }
 
-               /*
-                *      And the last bit.
-                */
-               if (*p) {
-                       XPUSHs(sv_2mortal(newSVpvn(p, strlen(p))));
+                       /*
+                        *      Multiple child values - create array and pass reference
+                        */
+                       av = newAV();
+                       perl_vblist_to_av(av, &arg->vb_group);
+                       DEBUG3("Passing list as array %pM", &arg->vb_group);
+                       sv = newRV_inc((SV *)av);
+                       XPUSHs(sv_2mortal(sv));
                }
 
                PUTBACK;
 
-               count = call_pv(inst->func_xlat, G_SCALAR | G_EVAL);
+               count = call_pv(func->vb_strvalue, G_ARRAY | G_EVAL);
 
                SPAGAIN;
                if (SvTRUE(ERRSV)) {
                        REDEBUG("Exit %s", SvPV(ERRSV,n_a));
                        (void)POPs;
-               } else if (count > 0) {
-                       tmp = POPp;
-                       strlcpy(*out, tmp, outlen);
-                       ret = strlen(*out);
+                       goto cleanup;
+               }
+
+               /*
+                *      As results are popped from a stack, they are in reverse
+                *      sequence.  Add to a temporary list and then prepend to
+                *      main list.
+                */
+               for (i = 0; i < count; i++) {
+                       sv = POPs;
+                       if (perl_sv_to_vblist(ctx, &sub_list, request, sv) < 0) goto cleanup;
+                       fr_dlist_move_head(&list, &sub_list);
+               }
+               ret = XLAT_ACTION_DONE;
 
-                       RDEBUG2("Len is %zu , out is %s freespace is %zu", ret, *out, outlen);
+               /*
+                *      Move the assembled list of boxes to the output
+                */
+               while ((vb = fr_dlist_pop_head(&list))) {
+                       fr_dcursor_append(out, vb);
                }
 
+       cleanup:
                PUTBACK ;
                FREETMPS ;
                LEAVE ;
@@ -507,13 +715,15 @@ static void perl_parse_config(CONF_SECTION *cs, int lvl, HV *rad_hv)
 static int mod_bootstrap(void *instance, CONF_SECTION *conf)
 {
        rlm_perl_t      *inst = instance;
-
+       xlat_t          *xlat;
        char const      *xlat_name;
 
        xlat_name = cf_section_name2(conf);
        if (!xlat_name) xlat_name = cf_section_name1(conf);
 
-       xlat_register_legacy(inst, xlat_name, perl_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN);
+       xlat = xlat_register(NULL, xlat_name, perl_xlat, false);
+       xlat_func_args(xlat, perl_xlat_args);
+       xlat_async_instantiate_set(xlat, mod_xlat_instantiate, rlm_perl_xlat_t, NULL, inst);
 
        return 0;
 }
diff --git a/src/tests/keywords/perl b/src/tests/keywords/perl
new file mode 100644 (file)
index 0000000..5d7d7eb
--- /dev/null
@@ -0,0 +1,97 @@
+#
+#  PRE: update
+#
+#  TODO
+#  A number of these tests return multiple values - either arrays, or hashes.
+#  Currently xlats have all returned values concatenated into a single value box -
+#  once multiple retuned boxes are handled to produce multiple attributes the tests
+#  will need amending.
+
+update request {
+       &Tmp-String-0 := "hello,goodbye,welcome,farewell"
+       &Tmp-String-1 := "foo,bar"
+}
+update request{
+       &Tmp-String-2 := "%(perl:xlat %{Tmp-String-0})"
+}
+
+if (&Tmp-String-2 != "hello|goodbye|welcome|farewell") {
+       test_fail
+}
+
+# Check for multiple arguments to perl function
+update request {
+       &Tmp-String-3 := "%(perl:xlat %{Tmp-String-0} %{Tmp-String-1})"
+}
+
+if (&Tmp-String-3 != "hello|goodbye|welcome|farewell#foo|bar") {
+       test_fail
+}
+
+update request {
+       &Tmp-Integer-0 := "%(perl:add 1 2 3)"
+}
+
+if (&Tmp-Integer-0 != 6) {
+       test_fail
+}
+
+
+update request {
+       &Tmp-String-0 := "hello"
+       &Tmp-String-0 += "goodbye"
+       &Tmp-String-0 += "welcome"
+}
+
+# Pass a list as an array
+update request {
+       &Tmp-String-2 := "%(perl:xlatarray %{Tmp-String-0[*]})"
+}
+
+if (&Tmp-String-2 != "hello|goodbye|welcome") {
+       test_fail
+}
+
+update request {
+       &Tmp-String-0 := "aa bb cc dd ee"
+}
+
+# Expect an array of returned values
+update request {
+       &Tmp-String-2 := "%(perl:xlatscalar2array %{Tmp-String-0})"
+}
+
+if (&Tmp-String-2 != "aabbccddee") {
+       test_fail
+}
+
+update request {
+       &Tmp-String-1 := "ff gg hh ii"
+}
+
+# Expect an array of array ref returned values
+update request {
+       &Tmp-String-2 := "%(perl:xlatscalar2arrayref %{Tmp-String-0} %{Tmp-String-1})"
+}
+
+if (&Tmp-String-2 != "aabbccddeeffgghhii") {
+       test_fail
+}
+
+update request {
+       &Tmp-String-0 := "a"
+       &Tmp-String-0 += "b"
+       &Tmp-String-0 += "c"
+}
+
+# Returns a hash with each element in the provided array converted to Vn->value
+update request {
+       &Tmp-String-2 := "%(perl:xlatarray2hash %{Tmp-String-0[*]})"
+}
+
+# Note: hashes don't have a defined sequence so these are all the possible return values
+if (&Tmp-String-2 != "V1aV2bV3c") && (&Tmp-String-2 != "V1aV3cV2b") && (&Tmp-String-2 != "V2bV1aV3c") && (&Tmp-String-2 != "V2bV3cV1a") && (&Tmp-String-2 != "V3cV1aV2b") && (&Tmp-String-2 != "V3cV2bV1a") {
+       test_fail
+}
+
+success
diff --git a/src/tests/keywords/perl.conf b/src/tests/keywords/perl.conf
new file mode 100644 (file)
index 0000000..0a8a0c7
--- /dev/null
@@ -0,0 +1,73 @@
+#
+# Test code for use testing xlat functionality of rlm_perl
+#
+
+use strict;
+use warnings;
+
+use constant {
+       L_AUTH         => 2,
+       L_INFO         => 3,
+       L_ERR          => 4,
+       L_WARN         => 5,
+       L_PROXY        => 6,
+       L_ACCT         => 7,
+       L_DBG          => 16,
+       L_DBG_WARN     => 17,
+       L_DBG_ERR      => 18,
+       L_DBG_WARN_REQ => 19,
+       L_DBG_ERR_REQ  => 20,
+};
+
+# Function to handle xlat
+# Just do something simple with all parameters provided and return to the caller
+sub xlat {
+
+       radiusd::log(L_DBG, 'From xlat '.join(' ', @_));
+
+       for (my $i = 0; $i <= $#_; $i++) {
+               $_[$i] = join('|', split(',', $_[$i]));
+       }
+
+       return join('#', @_);
+}
+
+# Simple function that returns an integer
+sub add {
+       my $ret = 0;
+       for (my $i = 0; $i <= $#_; $i++) {
+               $ret += $_[$i];
+       }
+       return $ret;
+}
+
+# Function which expects the first argument to be an array ref
+sub xlatarray{
+       return join('|', @{ $_[0] });
+}
+
+# Take a scalar and return an array
+sub xlatscalar2array {
+       return split(/ /, $_[0]);
+}
+
+# Take an arbitary number of scalars and retun an array of array refs
+sub xlatscalar2arrayref {
+       my @array;
+       for (my $i = 0; $i <= $#_; $i++) {
+               my @subarray = split(/ /, $_[$i]);
+               push (@array, \@subarray);
+       }
+       return @array;
+}
+
+# Function which receives an array and returns a hash
+sub xlatarray2hash {
+       my %hash;
+       my $i = 1;
+       foreach my $v (@{ $_[0] }) {
+               $hash{'V'.$i} = $v;
+               $i++;
+       }
+       return %hash;
+}
index 336d3753d792fe2fe173901e185368addb391ff1..995177e1dfa14cd9632baac27fd210c2df88582b 100644 (file)
@@ -62,6 +62,10 @@ modules {
 
        }
 
+       perl {
+               filename = ${keyword}/perl.conf
+               func_xlat = xlat
+       }
 }
 
 policy {
diff --git a/src/tests/modules/perl/auth.attrs b/src/tests/modules/perl/auth.attrs
new file mode 100644 (file)
index 0000000..91f0a86
--- /dev/null
@@ -0,0 +1,11 @@
+#
+#  Input packet
+#
+Packet-Type = Access-Request
+User-Name = "baduser"
+User-Password = "hello"
+
+#
+#  Expected answer
+#
+Packet-Type == Access-Accept
diff --git a/src/tests/modules/perl/auth.unlang b/src/tests/modules/perl/auth.unlang
new file mode 100644 (file)
index 0000000..aef2b3c
--- /dev/null
@@ -0,0 +1,36 @@
+#  Initial user will be rejected by the perl script.
+perl.authenticate
+
+#  fail has been used as the return code so the test doesn't exit.
+if (!notfound) {
+    test_fail
+}
+
+if (&reply.Reply-Message != "Denied access by rlm_perl function") {
+    test_fail
+}
+
+update reply {
+    &Reply-Message !* ANY
+}
+
+#  User will not be rejected - an xlat will be called setting a reply attribute
+update request {
+    &User-Name := 'bob'
+}
+
+perl.authenticate
+
+if (!ok) {
+    test_fail
+}
+
+if (&reply.Vendor-Specific.Cisco.h323-credit-amount != 100) {
+    test_fail
+}
+
+update reply {
+    &Vendor-Specific.Cisco.h323-credit-amount !* ANY
+}
+
+test_pass
index 2a5dc325defa9ea3f0ad862bc801727f2c144558..c4ea5b5f693be0f485fac876cc4234002a7a9e07 100644 (file)
@@ -22,3 +22,7 @@ perl {
 #              }
 #      }
 }
+
+client {
+
+}
\ No newline at end of file
index 9eecf91dcb44b11890be85d4ee196f8be83de72c..da244920d52fd1cbe47656e3a19a3a2197fbc33e 100644 (file)
@@ -67,14 +67,15 @@ sub authenticate {
        if ($RAD_REQUEST{'User-Name'} =~ /^baduser/i) {
                # Reject user and tell him why
                $RAD_REPLY{'Reply-Message'} = "Denied access by rlm_perl function";
-               return RLM_MODULE_REJECT;
+               # For testing return NOTFOUND - returning REJECT immediatly rejects the packet so fails the test
+               return RLM_MODULE_NOTFOUND;
        } else {
                # Accept user and set some attribute
                if (&radiusd::xlat("%(client:group)") eq 'UltraAllInclusive') {
                        # User called from NAS with unlim plan set, set higher limits
-                       $RAD_REPLY{'h323-credit-amount'} = "1000000";
+                       $RAD_REPLY{'Vendor-Specific.Cisco.h323-credit-amount'} = "1000000";
                } else {
-                       $RAD_REPLY{'h323-credit-amount'} = "100";
+                       $RAD_REPLY{'Vendor-Specific.Cisco.h323-credit-amount'} = "100";
                }
                return RLM_MODULE_OK;
        }
@@ -130,8 +131,8 @@ sub xlat {
 
        # Loads some external perl and evaluate it
        my ($filename,$a,$b,$c,$d) = @_;
-       radiusd::radlog(L_DBG, "From xlat $filename");
-       radiusd::radlog(L_DBG,"From xlat $a $b $c $d");
+       radiusd::log(L_DBG, "From xlat $filename");
+       radiusd::log(L_DBG,"From xlat $a $b $c $d");
        open(my $FH, '<', $filename) or die "open '$filename' $!";
        local($/) = undef;
        my $sub = <$FH>;
@@ -159,6 +160,6 @@ sub log_request_attributes {
        # This shouldn't be done in production environments!
        # This is only meant for debugging!
        for (keys %RAD_REQUEST) {
-               radiusd::radlog(L_DBG, "RAD_REQUEST: $_ = $RAD_REQUEST{$_}");
+               radiusd::log(L_DBG, "RAD_REQUEST: $_ = $RAD_REQUEST{$_}");
        }
 }