Przeglądaj źródła

fix(ldap): escape the escape character (\)

firest 2 lat temu
rodzic
commit
e07937a3ef

+ 6 - 1
apps/emqx_auth_ldap/test/emqx_authn_ldap_SUITE.erl

@@ -241,7 +241,12 @@ user_seeds() ->
         New(<<"mqttuser0006">>, <<"mqttuser0006">>, {error, user_disabled}),
         %% IsSuperuser
         New(<<"mqttuser0007">>, <<"mqttuser0007">>, {ok, #{is_superuser => true}}),
-        New(<<"mqttuser0008 (test)">>, <<"mqttuser0008 (test)">>, {ok, #{is_superuser => true}})
+        New(<<"mqttuser0008 (test)">>, <<"mqttuser0008 (test)">>, {ok, #{is_superuser => true}}),
+        New(
+            <<"mqttuser0009 \\\\test\\\\">>,
+            <<"mqttuser0009 \\\\test\\\\">>,
+            {ok, #{is_superuser => true}}
+        )
         | Valid
     ].
 

+ 1 - 1
apps/emqx_auth_ldap/test/emqx_authn_ldap_bind_SUITE.erl

@@ -235,7 +235,7 @@ user_seeds() ->
         lists:seq(1, 5)
     ),
 
-    Specials = [<<"mqttuser0008 (test)">>],
+    Specials = [<<"mqttuser0008 (test)">>, <<"mqttuser0009 \\\\test\\\\">>],
 
     Valid =
         lists:map(

+ 3 - 5
apps/emqx_ldap/src/emqx_ldap.erl

@@ -319,10 +319,8 @@ do_prepare_template([], State) ->
 
 filter_escape(Binary) when is_binary(Binary) ->
     filter_escape(erlang:binary_to_list(Binary));
-filter_escape([$\\ | T]) ->
-    [$\\, $\\ | filter_escape(T)];
 filter_escape([Char | T]) ->
-    case lists:member(Char, filter_control_chars()) of
+    case lists:member(Char, filter_special_chars()) of
         true ->
             [$\\, Char | filter_escape(T)];
         _ ->
@@ -331,5 +329,5 @@ filter_escape([Char | T]) ->
 filter_escape([]) ->
     [].
 
-filter_control_chars() ->
-    [$(, $), $&, $|, $=, $!, $~, $>, $<, $:, $*, $\t, $\n, $\r].
+filter_special_chars() ->
+    [$(, $), $&, $|, $=, $!, $~, $>, $<, $:, $*, $\t, $\n, $\r, $\\].

+ 19 - 4
apps/emqx_ldap/src/emqx_ldap_filter_lexer.xrl

@@ -2,8 +2,8 @@ Definitions.
 
 Control = [()&|!=~><:*]
 White = [\s\t\n\r]+
-StringChars = [^()&|!=~><:*\t\n\r]
-Escape = \\{Control}|\\{White}
+StringChars = [^()&|!=~><:*\t\n\r\\]
+Escape = \\\\|\\{Control}|\\{White}
 String = ({Escape}|{StringChars})+
 
 Rules.
@@ -23,7 +23,7 @@ Rules.
 {White} : skip_token.
 {String} : {token, {string, TokenLine, to_string(TokenChars)}}.
 %% Leex will hang if a composite operation is missing a character
-{Control} : {error, lists:flatten(io_lib:format("Unexpected Tokens:~ts", [TokenChars]))}.
+{Control} : {error, format("Unexpected Tokens:~ts", [TokenChars])}.
 
 Erlang code.
 
@@ -34,4 +34,19 @@ Erlang code.
 %% so after the tokenization we should remove all escape character
 to_string(TokenChars) ->
     String = string:trim(TokenChars),
-    lists:flatten(string:replace(String, "\\", "", all)).
+    trim_escape(String).
+
+%% because of the below situation, we can't directly use the `replace` to trim the escape character
+%%trim_escape([$\\, $\\ | T]) ->
+%%    [$\\ | trim_escape(T)];
+trim_escape([$\\, Char | T]) ->
+    [Char | trim_escape(T)];
+%% the underneath is impossible to occur because it is not valid in the lexer
+%% trim_escape([$\\])
+trim_escape([Char | T]) ->
+    [Char | trim_escape(T)];
+trim_escape([]) ->
+    [].
+
+format(Fmt, Args) ->
+    lists:flatten(io_lib:format(Fmt, Args)).

+ 9 - 0
apps/emqx_ldap/test/data/emqx.io.ldif

@@ -166,6 +166,15 @@ uid: mqttuser0008 (test)
 isSuperuser: TRUE
 userPassword: {SHA}FCzJLOp66OwsZ9DQzXSxdTd9c0U=
 
+objectClass: top
+dn:uid=mqttuser0009 \\test\\,ou=testdevice,dc=emqx,dc=io
+objectClass: mqttUser
+objectClass: mqttDevice
+objectClass: mqttSecurity
+uid: mqttuser0009 \\test\\
+isSuperuser: TRUE
+userPassword: {SHA}awxXARLqWYx+xy0677D/TLjlyHA=
+
 ## Try to test with base DN 'ou=dashboard,dc=emqx,dc=io'
 ## with a filter ugroup=group1
 ## this should return 2 users in the query and fail the test

+ 4 - 0
apps/emqx_ldap/test/emqx_ldap_filter_SUITE.erl

@@ -235,6 +235,10 @@ t_escape(_Config) ->
     ?assertEqual(
         'or'([equalityMatch("a", "name (1) *")]),
         parse("(|(a=name\\ \\(1\\) \\*))")
+    ),
+    ?assertEqual(
+        'and'([equalityMatch("a", "\\value\\")]),
+        parse("(&(a=\\\\value\\\\))")
     ).
 
 t_value_eql_dn(_Config) ->