emqx_authn_ldap_bind_SUITE.erl 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2023-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
  3. %%
  4. %% Licensed under the Apache License, Version 2.0 (the "License");
  5. %% you may not use this file except in compliance with the License.
  6. %% You may obtain a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing, software
  11. %% distributed under the License is distributed on an "AS IS" BASIS,
  12. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. %% See the License for the specific language governing permissions and
  14. %% limitations under the License.
  15. %%--------------------------------------------------------------------
  16. -module(emqx_authn_ldap_bind_SUITE).
  17. -compile(nowarn_export_all).
  18. -compile(export_all).
  19. -include_lib("emqx_auth/include/emqx_authn.hrl").
  20. -include_lib("eunit/include/eunit.hrl").
  21. -include_lib("common_test/include/ct.hrl").
  22. -define(LDAP_HOST, "ldap").
  23. -define(LDAP_DEFAULT_PORT, 389).
  24. -define(LDAP_RESOURCE, <<"emqx_authn_ldap_bind_SUITE">>).
  25. -define(PATH, [authentication]).
  26. -define(ResourceID, <<"password_based:ldap">>).
  27. all() ->
  28. emqx_common_test_helpers:all(?MODULE).
  29. init_per_testcase(_, Config) ->
  30. emqx_authn_test_lib:delete_authenticators(
  31. [authentication],
  32. ?GLOBAL
  33. ),
  34. Config.
  35. init_per_suite(Config) ->
  36. _ = application:load(emqx_conf),
  37. case emqx_common_test_helpers:is_tcp_server_available(?LDAP_HOST, ?LDAP_DEFAULT_PORT) of
  38. true ->
  39. Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_auth, emqx_auth_ldap], #{
  40. work_dir => ?config(priv_dir, Config)
  41. }),
  42. {ok, _} = emqx_resource:create_local(
  43. ?LDAP_RESOURCE,
  44. ?AUTHN_RESOURCE_GROUP,
  45. emqx_ldap,
  46. ldap_config(),
  47. #{}
  48. ),
  49. [{apps, Apps} | Config];
  50. false ->
  51. {skip, no_ldap}
  52. end.
  53. end_per_suite(Config) ->
  54. emqx_authn_test_lib:delete_authenticators(
  55. [authentication],
  56. ?GLOBAL
  57. ),
  58. ok = emqx_resource:remove_local(?LDAP_RESOURCE),
  59. ok = emqx_cth_suite:stop(?config(apps, Config)).
  60. %%------------------------------------------------------------------------------
  61. %% Tests
  62. %%------------------------------------------------------------------------------
  63. t_create(_Config) ->
  64. AuthConfig = raw_ldap_auth_config(),
  65. {ok, _} = emqx:update_config(
  66. ?PATH,
  67. {create_authenticator, ?GLOBAL, AuthConfig}
  68. ),
  69. {ok, [#{provider := emqx_authn_ldap}]} = emqx_authn_chains:list_authenticators(?GLOBAL),
  70. emqx_authn_test_lib:delete_config(?ResourceID).
  71. t_create_invalid(_Config) ->
  72. AuthConfig = raw_ldap_auth_config(),
  73. InvalidConfigs =
  74. [
  75. AuthConfig#{<<"server">> => <<"unknownhost:3333">>},
  76. AuthConfig#{<<"password">> => <<"wrongpass">>}
  77. ],
  78. lists:foreach(
  79. fun(Config) ->
  80. {ok, _} = emqx:update_config(
  81. ?PATH,
  82. {create_authenticator, ?GLOBAL, Config}
  83. ),
  84. emqx_authn_test_lib:delete_config(?ResourceID),
  85. ?assertEqual(
  86. {error, {not_found, {chain, ?GLOBAL}}},
  87. emqx_authn_chains:list_authenticators(?GLOBAL)
  88. )
  89. end,
  90. InvalidConfigs
  91. ).
  92. t_authenticate(_Config) ->
  93. ok = lists:foreach(
  94. fun(Sample) ->
  95. ct:pal("test_user_auth sample: ~p", [Sample]),
  96. test_user_auth(Sample)
  97. end,
  98. user_seeds()
  99. ).
  100. test_user_auth(#{
  101. credentials := Credentials0,
  102. config_params := SpecificConfigParams,
  103. result := Result
  104. }) ->
  105. AuthConfig = maps:merge(raw_ldap_auth_config(), SpecificConfigParams),
  106. {ok, _} = emqx:update_config(
  107. ?PATH,
  108. {create_authenticator, ?GLOBAL, AuthConfig}
  109. ),
  110. Credentials = Credentials0#{
  111. listener => 'tcp:default',
  112. protocol => mqtt
  113. },
  114. ?assertEqual(Result, emqx_access_control:authenticate(Credentials)),
  115. emqx_authn_test_lib:delete_authenticators(
  116. [authentication],
  117. ?GLOBAL
  118. ).
  119. t_destroy(_Config) ->
  120. AuthConfig = raw_ldap_auth_config(),
  121. {ok, _} = emqx:update_config(
  122. ?PATH,
  123. {create_authenticator, ?GLOBAL, AuthConfig}
  124. ),
  125. {ok, [#{provider := emqx_authn_ldap, state := State}]} =
  126. emqx_authn_chains:list_authenticators(?GLOBAL),
  127. {ok, _} = emqx_authn_ldap:authenticate(
  128. #{
  129. username => <<"mqttuser0001">>,
  130. password => <<"mqttuser0001">>
  131. },
  132. State
  133. ),
  134. emqx_authn_test_lib:delete_authenticators(
  135. [authentication],
  136. ?GLOBAL
  137. ),
  138. % Authenticator should not be usable anymore
  139. ?assertMatch(
  140. ignore,
  141. emqx_authn_ldap:authenticate(
  142. #{
  143. username => <<"mqttuser0001">>,
  144. password => <<"mqttuser0001">>
  145. },
  146. State
  147. )
  148. ).
  149. t_update(_Config) ->
  150. CorrectConfig = raw_ldap_auth_config(),
  151. IncorrectConfig =
  152. CorrectConfig#{
  153. <<"base_dn">> => <<"ou=testdevice,dc=emqx,dc=io">>,
  154. <<"filter">> => <<"(objectClass=mqttUser)">>
  155. },
  156. {ok, _} = emqx:update_config(
  157. ?PATH,
  158. {create_authenticator, ?GLOBAL, IncorrectConfig}
  159. ),
  160. {error, _} = emqx_access_control:authenticate(
  161. #{
  162. username => <<"mqttuser0001">>,
  163. password => <<"mqttuser0001">>,
  164. listener => 'tcp:default',
  165. protocol => mqtt
  166. }
  167. ),
  168. % We update with config with correct query, provider should update and work properly
  169. {ok, _} = emqx:update_config(
  170. ?PATH,
  171. {update_authenticator, ?GLOBAL, <<"password_based:ldap">>, CorrectConfig}
  172. ),
  173. {ok, _} = emqx_access_control:authenticate(
  174. #{
  175. username => <<"mqttuser0001">>,
  176. password => <<"mqttuser0001">>,
  177. listener => 'tcp:default',
  178. protocol => mqtt
  179. }
  180. ).
  181. %%------------------------------------------------------------------------------
  182. %% Helpers
  183. %%------------------------------------------------------------------------------
  184. raw_ldap_auth_config() ->
  185. #{
  186. <<"mechanism">> => <<"password_based">>,
  187. <<"backend">> => <<"ldap">>,
  188. <<"server">> => ldap_server(),
  189. <<"base_dn">> => <<"ou=testdevice,dc=emqx,dc=io">>,
  190. <<"filter">> => <<"(uid=${username})">>,
  191. <<"username">> => <<"cn=root,dc=emqx,dc=io">>,
  192. <<"password">> => <<"public">>,
  193. <<"pool_size">> => 8,
  194. <<"method">> => #{
  195. <<"type">> => <<"bind">>,
  196. <<"bind_password">> => <<"${password}">>
  197. }
  198. }.
  199. user_seeds() ->
  200. New = fun(Username, Password, Result) ->
  201. #{
  202. credentials => #{
  203. username => Username,
  204. password => Password
  205. },
  206. config_params => #{},
  207. result => Result
  208. }
  209. end,
  210. Normal = lists:map(
  211. fun(Idx) ->
  212. erlang:iolist_to_binary(io_lib:format("mqttuser000~b", [Idx]))
  213. end,
  214. lists:seq(1, 5)
  215. ),
  216. Specials = [<<"mqttuser0008 (test)">>, <<"mqttuser0009 \\\\test\\\\">>],
  217. Valid =
  218. lists:map(
  219. fun(Username) ->
  220. New(Username, Username, {ok, #{is_superuser => false}})
  221. end,
  222. Normal ++ Specials
  223. ),
  224. [
  225. %% Not exists
  226. New(<<"notexists">>, <<"notexists">>, {error, not_authorized}),
  227. %% Wrong Password
  228. New(<<"mqttuser0001">>, <<"wrongpassword">>, {error, bad_username_or_password})
  229. | Valid
  230. ].
  231. ldap_server() ->
  232. iolist_to_binary(io_lib:format("~s:~B", [?LDAP_HOST, ?LDAP_DEFAULT_PORT])).
  233. ldap_config() ->
  234. emqx_ldap_SUITE:ldap_config([]).