Quellcode durchsuchen

feat: add 'is_empty_val' and 'not' functions to variform

zmstone vor 1 Jahr
Ursprung
Commit
ca87f0a43e

+ 2 - 2
apps/emqx_auth_cinfo/README.md

@@ -4,7 +4,7 @@ This application implements an extended authentication for EMQX Enterprise editi
 
 
 Client-info (of type `cinfo`) authentication is a lightweight authentication mechanism which checks client properties and attributes against user defined rules.
 Client-info (of type `cinfo`) authentication is a lightweight authentication mechanism which checks client properties and attributes against user defined rules.
 The rules make use of the Variform expression to define match conditions, and the authentication result when match is found.
 The rules make use of the Variform expression to define match conditions, and the authentication result when match is found.
-For example, to quickly fencing off clients without a username, the match condition can be `str_eq(username, '')` associated with a attributes result `deny`.
+For example, to quickly fencing off clients without a username, the match condition can be `is_empty_val(username)` associated with a attributes result `deny`.
 
 
 The new authenticator config look is like below.
 The new authenticator config look is like below.
 
 
@@ -21,7 +21,7 @@ authentication = [
       # deny clients with empty username and client ID starts with 'v1-'
       # deny clients with empty username and client ID starts with 'v1-'
       {
       {
         # when is_match is an array, it yields 'true' if all individual checks yield 'true'
         # when is_match is an array, it yields 'true' if all individual checks yield 'true'
-        is_match = ["str_eq(username, '')", "str_eq(nth(1,tokens(clientid,'-')), 'v1')"]
+        is_match = ["is_empty_val(username)", "str_eq(nth(1,tokens(clientid,'-')), 'v1')"]
         result = deny
         result = deny
       }
       }
       # if all checks are exhausted without an 'allow' or a 'deny' result, continue to the next authentication
       # if all checks are exhausted without an 'allow' or a 'deny' result, continue to the next authentication

+ 2 - 2
apps/emqx_auth_cinfo/test/emqx_authn_cinfo_SUITE.erl

@@ -42,7 +42,7 @@ t_username_equal_clientid(_) ->
     Checks =
     Checks =
         [
         [
             #{
             #{
-                is_match => <<"str_eq(username, '')">>,
+                is_match => <<"is_empty_val(username)">>,
                 result => deny
                 result => deny
             },
             },
             #{
             #{
@@ -105,7 +105,7 @@ t_multiple_is_match_expressions(_) ->
             %% use AND to connect multiple is_match expressions
             %% use AND to connect multiple is_match expressions
             %% this one means username is not empty, and clientid is 'super'
             %% this one means username is not empty, and clientid is 'super'
             is_match => [
             is_match => [
-                <<"str_neq('', username)">>, <<"str_eq(clientid, 'super')">>
+                <<"not(is_empty_val(username))">>, <<"str_eq(clientid, 'super')">>
             ],
             ],
             result => allow
             result => allow
         }
         }

+ 16 - 1
apps/emqx_utils/src/emqx_variform_bif.erl

@@ -53,7 +53,9 @@
     join_to_string/1,
     join_to_string/1,
     join_to_string/2,
     join_to_string/2,
     unescape/1,
     unescape/1,
-    any_to_str/1
+    any_to_str/1,
+    is_empty_val/1,
+    'not'/1
 ]).
 ]).
 
 
 %% Array functions
 %% Array functions
@@ -594,6 +596,19 @@ num_gte(A, B) ->
     R = num_comp(A, B),
     R = num_comp(A, B),
     R =:= gt orelse R =:= eq.
     R =:= gt orelse R =:= eq.
 
 
+%% @doc Return 'true' if the argument is `undefined`, `null` or empty string, or empty array.
+is_empty_val(undefined) -> true;
+is_empty_val(null) -> true;
+is_empty_val(<<>>) -> true;
+is_empty_val([]) -> true;
+is_empty_val(_) -> false.
+
+%% @doc The 'not' operation for boolean values and strings.
+'not'(true) -> false;
+'not'(false) -> true;
+'not'(<<"true">>) -> <<"false">>;
+'not'(<<"false">>) -> <<"true">>.
+
 %%------------------------------------------------------------------------------
 %%------------------------------------------------------------------------------
 %% System
 %% System
 %%------------------------------------------------------------------------------
 %%------------------------------------------------------------------------------

+ 21 - 0
apps/emqx_utils/test/emqx_variform_bif_tests.erl

@@ -79,3 +79,24 @@ system_test() ->
     EnvNameBin = erlang:list_to_binary(EnvName),
     EnvNameBin = erlang:list_to_binary(EnvName),
     os:putenv("EMQXVAR_" ++ EnvName, EnvVal),
     os:putenv("EMQXVAR_" ++ EnvName, EnvVal),
     ?assertEqual(erlang:list_to_binary(EnvVal), emqx_variform_bif:getenv(EnvNameBin)).
     ?assertEqual(erlang:list_to_binary(EnvVal), emqx_variform_bif:getenv(EnvNameBin)).
+
+empty_val_test_() ->
+    F = fun(X) -> emqx_variform_bif:is_empty_val(X) end,
+    [
+        ?_assert(F(undefined)),
+        ?_assert(F(null)),
+        ?_assert(F(<<>>)),
+        ?_assert(F([])),
+        ?_assertNot(F(true)),
+        ?_assertNot(F(false)),
+        ?_assertNot(F(<<"a">>))
+    ].
+
+bool_not_test_() ->
+    Not = fun(X) -> emqx_variform_bif:'not'(X) end,
+    [
+        ?_assertEqual(<<"false">>, Not(<<"true">>)),
+        ?_assertEqual(<<"true">>, Not(<<"false">>)),
+        ?_assertEqual(true, Not(false)),
+        ?_assertEqual(false, Not(true))
+    ].

+ 1 - 1
changes/ee/feat-13810.en.md

@@ -2,4 +2,4 @@ Add clinet-info authentication.
 
 
 Client-info (of type `cinfo`) authentication is a lightweight authentication mechanism which checks client properties and attributes against user defined rules.
 Client-info (of type `cinfo`) authentication is a lightweight authentication mechanism which checks client properties and attributes against user defined rules.
 The rules make use of the Variform expression to define match conditions, and the authentication result when match is found.
 The rules make use of the Variform expression to define match conditions, and the authentication result when match is found.
-For example, to quickly fence off clients without a username, the match condition can be `str_eq(username, '')` associated with a check result `deny`.
+For example, to quickly fence off clients without a username, the match condition can be `is_empty_val(username)` associated with a check result `deny`.

+ 1 - 1
rel/config/ee-examples/cinfo-authn.conf

@@ -10,7 +10,7 @@ authentication = [
       # deny clients with empty username and client ID starts with 'v1-'
       # deny clients with empty username and client ID starts with 'v1-'
       {
       {
         # when is_match is an array, it yields 'true' if all individual checks yield 'true'
         # when is_match is an array, it yields 'true' if all individual checks yield 'true'
-        is_match = ["str_eq(username, '')", "str_eq(nth(1,tokens(clientid,'-')), 'v1')"]
+        is_match = ["is_empty_val(username)", "str_eq(nth(1,tokens(clientid,'-')), 'v1')"]
         result = deny
         result = deny
       }
       }
       # if all checks are exhausted without an 'allow' or a 'deny' result, continue to the next authentication
       # if all checks are exhausted without an 'allow' or a 'deny' result, continue to the next authentication