emqx_authn_api_SUITE.erl 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2021-2023 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. %% http://www.apache.org/licenses/LICENSE-2.0
  8. %%
  9. %% Unless required by applicable law or agreed to in writing, software
  10. %% distributed under the License is distributed on an "AS IS" BASIS,
  11. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. %% See the License for the specific language governing permissions and
  13. %% limitations under the License.
  14. %%--------------------------------------------------------------------
  15. -module(emqx_authn_api_SUITE).
  16. -compile(nowarn_export_all).
  17. -compile(export_all).
  18. -import(emqx_dashboard_api_test_helpers, [multipart_formdata_request/3]).
  19. -import(emqx_mgmt_api_test_util, [request/3, uri/1]).
  20. -include("emqx_authn.hrl").
  21. -include_lib("eunit/include/eunit.hrl").
  22. -define(TCP_DEFAULT, 'tcp:default').
  23. -define(assertAuthenticatorsMatch(Guard, Path),
  24. (fun() ->
  25. {ok, 200, Response} = request(get, uri(Path)),
  26. ?assertMatch(Guard, emqx_utils_json:decode(Response, [return_maps]))
  27. end)()
  28. ).
  29. all() ->
  30. emqx_common_test_helpers:all(?MODULE).
  31. groups() ->
  32. [].
  33. init_per_testcase(t_authenticator_fail, Config) ->
  34. meck:expect(emqx_authn_proto_v1, lookup_from_all_nodes, 3, [{error, {exception, badarg}}]),
  35. init_per_testcase(default, Config);
  36. init_per_testcase(_Case, Config) ->
  37. {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
  38. emqx_authn_test_lib:delete_authenticators(
  39. [?CONF_NS_ATOM],
  40. ?GLOBAL
  41. ),
  42. emqx_authn_test_lib:delete_authenticators(
  43. [listeners, tcp, default, ?CONF_NS_ATOM],
  44. ?TCP_DEFAULT
  45. ),
  46. {atomic, ok} = mria:clear_table(emqx_authn_mnesia),
  47. Config.
  48. end_per_testcase(t_authenticator_fail, Config) ->
  49. meck:unload(emqx_authn_proto_v1),
  50. Config;
  51. end_per_testcase(_, Config) ->
  52. Config.
  53. init_per_suite(Config) ->
  54. emqx_config:erase(?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY),
  55. _ = application:load(emqx_conf),
  56. ok = emqx_mgmt_api_test_util:init_suite(
  57. [emqx_conf, emqx_authn]
  58. ),
  59. ?AUTHN:delete_chain(?GLOBAL),
  60. {ok, Chains} = ?AUTHN:list_chains(),
  61. ?assertEqual(length(Chains), 0),
  62. Config.
  63. end_per_suite(_Config) ->
  64. emqx_mgmt_api_test_util:end_suite([emqx_authn]),
  65. ok.
  66. %%------------------------------------------------------------------------------
  67. %% Tests
  68. %%------------------------------------------------------------------------------
  69. t_invalid_listener(_) ->
  70. {ok, 404, _} = request(get, uri(["listeners", "invalid", ?CONF_NS])),
  71. {ok, 404, _} = request(get, uri(["listeners", "in:valid", ?CONF_NS])).
  72. t_authenticators(_) ->
  73. test_authenticators([]).
  74. t_authenticator(_) ->
  75. test_authenticator([]).
  76. t_authenticator_fail(_) ->
  77. ValidConfig0 = emqx_authn_test_lib:http_example(),
  78. {ok, 200, _} = request(
  79. post,
  80. uri([?CONF_NS]),
  81. ValidConfig0
  82. ),
  83. ?assertMatch(
  84. {ok, 500, _},
  85. request(
  86. get,
  87. uri([?CONF_NS, "password_based:http", "status"])
  88. )
  89. ).
  90. t_authenticator_users(_) ->
  91. test_authenticator_users([]).
  92. t_authenticator_user(_) ->
  93. test_authenticator_user([]).
  94. t_authenticator_position(_) ->
  95. test_authenticator_position([]).
  96. t_authenticator_import_users(_) ->
  97. test_authenticator_import_users([]).
  98. %t_listener_authenticators(_) ->
  99. % test_authenticators(["listeners", ?TCP_DEFAULT]).
  100. %t_listener_authenticator(_) ->
  101. % test_authenticator(["listeners", ?TCP_DEFAULT]).
  102. %t_listener_authenticator_users(_) ->
  103. % test_authenticator_users(["listeners", ?TCP_DEFAULT]).
  104. %t_listener_authenticator_user(_) ->
  105. % test_authenticator_user(["listeners", ?TCP_DEFAULT]).
  106. %t_listener_authenticator_position(_) ->
  107. % test_authenticator_position(["listeners", ?TCP_DEFAULT]).
  108. %t_listener_authenticator_import_users(_) ->
  109. % test_authenticator_import_users(["listeners", ?TCP_DEFAULT]).
  110. t_aggregate_metrics(_) ->
  111. Metrics = #{
  112. 'emqx@node1.emqx.io' => #{
  113. metrics =>
  114. #{
  115. failed => 0,
  116. total => 1,
  117. rate => 0.0,
  118. rate_last5m => 0.0,
  119. rate_max => 0.1,
  120. success => 1,
  121. nomatch => 1
  122. }
  123. },
  124. 'emqx@node2.emqx.io' => #{
  125. metrics =>
  126. #{
  127. failed => 0,
  128. total => 1,
  129. rate => 0.0,
  130. rate_last5m => 0.0,
  131. rate_max => 0.1,
  132. success => 1,
  133. nomatch => 2
  134. }
  135. }
  136. },
  137. Res = emqx_authn_api:aggregate_metrics(maps:values(Metrics)),
  138. ?assertEqual(
  139. #{
  140. metrics =>
  141. #{
  142. failed => 0,
  143. total => 2,
  144. rate => 0.0,
  145. rate_last5m => 0.0,
  146. rate_max => 0.2,
  147. success => 2,
  148. nomatch => 3
  149. }
  150. },
  151. Res
  152. ).
  153. test_authenticators(PathPrefix) ->
  154. ValidConfig = emqx_authn_test_lib:http_example(),
  155. {ok, 200, _} = request(
  156. post,
  157. uri(PathPrefix ++ [?CONF_NS]),
  158. ValidConfig
  159. ),
  160. {ok, 409, _} = request(
  161. post,
  162. uri(PathPrefix ++ [?CONF_NS]),
  163. ValidConfig
  164. ),
  165. InvalidConfig0 = ValidConfig#{method => <<"delete">>},
  166. {ok, 400, _} = request(
  167. post,
  168. uri(PathPrefix ++ [?CONF_NS]),
  169. InvalidConfig0
  170. ),
  171. ValidConfig1 = ValidConfig#{
  172. method => <<"get">>,
  173. headers => #{<<"content-type">> => <<"application/json">>}
  174. },
  175. {ok, 204, _} = request(
  176. put,
  177. uri(PathPrefix ++ [?CONF_NS, "password_based:http"]),
  178. ValidConfig1
  179. ),
  180. ?assertAuthenticatorsMatch(
  181. [#{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}],
  182. PathPrefix ++ [?CONF_NS]
  183. ).
  184. test_authenticator(PathPrefix) ->
  185. ValidConfig0 = emqx_authn_test_lib:http_example(),
  186. {ok, 200, _} = request(
  187. post,
  188. uri(PathPrefix ++ [?CONF_NS]),
  189. ValidConfig0
  190. ),
  191. {ok, 200, _} = request(
  192. get,
  193. uri(PathPrefix ++ [?CONF_NS, "password_based:http"])
  194. ),
  195. {ok, 200, Res} = request(
  196. get,
  197. uri(PathPrefix ++ [?CONF_NS, "password_based:http", "status"])
  198. ),
  199. {ok, RList} = emqx_utils_json:safe_decode(Res),
  200. Snd = fun({_, Val}) -> Val end,
  201. LookupVal = fun LookupV(List, RestJson) ->
  202. case List of
  203. [Name] -> Snd(lists:keyfind(Name, 1, RestJson));
  204. [Name | NS] -> LookupV(NS, Snd(lists:keyfind(Name, 1, RestJson)))
  205. end
  206. end,
  207. LookFun = fun(List) -> LookupVal(List, RList) end,
  208. MetricsList = [
  209. {<<"failed">>, 0},
  210. {<<"total">>, 0},
  211. {<<"rate">>, 0.0},
  212. {<<"rate_last5m">>, 0.0},
  213. {<<"rate_max">>, 0.0},
  214. {<<"success">>, 0}
  215. ],
  216. EqualFun = fun({M, V}) ->
  217. ?assertEqual(
  218. V,
  219. LookFun([
  220. <<"metrics">>,
  221. M
  222. ])
  223. )
  224. end,
  225. lists:map(EqualFun, MetricsList),
  226. ?assertEqual(
  227. <<"connected">>,
  228. LookFun([<<"status">>])
  229. ),
  230. ?assertMatch(
  231. {ok, 404, _},
  232. request(
  233. get,
  234. uri(PathPrefix ++ [?CONF_NS, "unknown_auth_chain", "status"])
  235. )
  236. ),
  237. {ok, 404, _} = request(
  238. get,
  239. uri(PathPrefix ++ [?CONF_NS, "password_based:redis"])
  240. ),
  241. {ok, 404, _} = request(
  242. put,
  243. uri(PathPrefix ++ [?CONF_NS, "password_based:built_in_database"]),
  244. emqx_authn_test_lib:built_in_database_example()
  245. ),
  246. InvalidConfig0 = ValidConfig0#{method => <<"delete">>},
  247. {ok, 400, _} = request(
  248. put,
  249. uri(PathPrefix ++ [?CONF_NS, "password_based:http"]),
  250. InvalidConfig0
  251. ),
  252. ValidConfig1 = ValidConfig0#{
  253. method => <<"get">>,
  254. headers => #{<<"content-type">> => <<"application/json">>}
  255. },
  256. {ok, 204, _} = request(
  257. put,
  258. uri(PathPrefix ++ [?CONF_NS, "password_based:http"]),
  259. ValidConfig1
  260. ),
  261. ValidConfig2 = ValidConfig0#{pool_size => 9},
  262. {ok, 204, _} = request(
  263. put,
  264. uri(PathPrefix ++ [?CONF_NS, "password_based:http"]),
  265. ValidConfig2
  266. ),
  267. {ok, 404, _} = request(
  268. delete,
  269. uri(PathPrefix ++ [?CONF_NS, "password_based:redis"])
  270. ),
  271. {ok, 204, _} = request(
  272. delete,
  273. uri(PathPrefix ++ [?CONF_NS, "password_based:http"])
  274. ),
  275. ?assertAuthenticatorsMatch([], PathPrefix ++ [?CONF_NS]).
  276. test_authenticator_users(PathPrefix) ->
  277. UsersUri = uri(PathPrefix ++ [?CONF_NS, "password_based:built_in_database", "users"]),
  278. {ok, 200, _} = request(
  279. post,
  280. uri(PathPrefix ++ [?CONF_NS]),
  281. emqx_authn_test_lib:built_in_database_example()
  282. ),
  283. {ok, Client} = emqtt:start_link(
  284. [
  285. {username, <<"u_event">>},
  286. {clientid, <<"c_event">>},
  287. {proto_ver, v5},
  288. {properties, #{'Session-Expiry-Interval' => 60}}
  289. ]
  290. ),
  291. process_flag(trap_exit, true),
  292. ?assertMatch({error, _}, emqtt:connect(Client)),
  293. timer:sleep(300),
  294. UsersUri0 = uri(PathPrefix ++ [?CONF_NS, "password_based:built_in_database", "status"]),
  295. {ok, 200, PageData0} = request(get, UsersUri0),
  296. case PathPrefix of
  297. [] ->
  298. #{
  299. <<"metrics">> := #{
  300. <<"total">> := 1,
  301. <<"success">> := 0,
  302. <<"nomatch">> := 1
  303. }
  304. } = emqx_utils_json:decode(PageData0, [return_maps]);
  305. ["listeners", 'tcp:default'] ->
  306. #{
  307. <<"metrics">> := #{
  308. <<"total">> := 1,
  309. <<"success">> := 0,
  310. <<"nomatch">> := 1
  311. }
  312. } = emqx_utils_json:decode(PageData0, [return_maps])
  313. end,
  314. InvalidUsers = [
  315. #{clientid => <<"u1">>, password => <<"p1">>},
  316. #{user_id => <<"u2">>},
  317. #{user_id => <<"u3">>, password => <<"p3">>, foobar => <<"foobar">>}
  318. ],
  319. lists:foreach(
  320. fun(User) -> {ok, 400, _} = request(post, UsersUri, User) end,
  321. InvalidUsers
  322. ),
  323. ValidUsers = [
  324. #{user_id => <<"u1">>, password => <<"p1">>},
  325. #{user_id => <<"u2">>, password => <<"p2">>, is_superuser => true},
  326. #{user_id => <<"u3">>, password => <<"p3">>}
  327. ],
  328. lists:foreach(
  329. fun(User) ->
  330. {ok, 201, UserData} = request(post, UsersUri, User),
  331. CreatedUser = emqx_utils_json:decode(UserData, [return_maps]),
  332. ?assertMatch(#{<<"user_id">> := _}, CreatedUser)
  333. end,
  334. ValidUsers
  335. ),
  336. {ok, Client1} = emqtt:start_link(
  337. [
  338. {username, <<"u1">>},
  339. {password, <<"p1">>},
  340. {clientid, <<"c_event">>},
  341. {proto_ver, v5},
  342. {properties, #{'Session-Expiry-Interval' => 60}}
  343. ]
  344. ),
  345. {ok, _} = emqtt:connect(Client1),
  346. timer:sleep(300),
  347. UsersUri01 = uri(PathPrefix ++ [?CONF_NS, "password_based:built_in_database", "status"]),
  348. {ok, 200, PageData01} = request(get, UsersUri01),
  349. case PathPrefix of
  350. [] ->
  351. #{
  352. <<"metrics">> := #{
  353. <<"total">> := 2,
  354. <<"success">> := 1,
  355. <<"nomatch">> := 1
  356. }
  357. } = emqx_utils_json:decode(PageData01, [return_maps]);
  358. ["listeners", 'tcp:default'] ->
  359. #{
  360. <<"metrics">> := #{
  361. <<"total">> := 2,
  362. <<"success">> := 1,
  363. <<"nomatch">> := 1
  364. }
  365. } = emqx_utils_json:decode(PageData01, [return_maps])
  366. end,
  367. {ok, 200, Page1Data} = request(get, UsersUri ++ "?page=1&limit=2"),
  368. #{
  369. <<"data">> := Page1Users,
  370. <<"meta">> :=
  371. #{
  372. <<"page">> := 1,
  373. <<"limit">> := 2,
  374. <<"count">> := 3
  375. }
  376. } =
  377. emqx_utils_json:decode(Page1Data, [return_maps]),
  378. {ok, 200, Page2Data} = request(get, UsersUri ++ "?page=2&limit=2"),
  379. #{
  380. <<"data">> := Page2Users,
  381. <<"meta">> :=
  382. #{
  383. <<"page">> := 2,
  384. <<"limit">> := 2,
  385. <<"count">> := 3
  386. }
  387. } = emqx_utils_json:decode(Page2Data, [return_maps]),
  388. ?assertEqual(2, length(Page1Users)),
  389. ?assertEqual(1, length(Page2Users)),
  390. ?assertEqual(
  391. [<<"u1">>, <<"u2">>, <<"u3">>],
  392. lists:usort([UserId || #{<<"user_id">> := UserId} <- Page1Users ++ Page2Users])
  393. ),
  394. {ok, 200, Super1Data} = request(get, UsersUri ++ "?page=1&limit=3&is_superuser=true"),
  395. #{
  396. <<"data">> := Super1Users,
  397. <<"meta">> :=
  398. #{
  399. <<"page">> := 1,
  400. <<"limit">> := 3,
  401. <<"count">> := 1
  402. }
  403. } = emqx_utils_json:decode(Super1Data, [return_maps]),
  404. ?assertEqual(
  405. [<<"u2">>],
  406. lists:usort([UserId || #{<<"user_id">> := UserId} <- Super1Users])
  407. ),
  408. {ok, 200, Super2Data} = request(get, UsersUri ++ "?page=1&limit=3&is_superuser=false"),
  409. #{
  410. <<"data">> := Super2Users,
  411. <<"meta">> :=
  412. #{
  413. <<"page">> := 1,
  414. <<"limit">> := 3,
  415. <<"count">> := 2
  416. }
  417. } = emqx_utils_json:decode(Super2Data, [return_maps]),
  418. ?assertEqual(
  419. [<<"u1">>, <<"u3">>],
  420. lists:usort([UserId || #{<<"user_id">> := UserId} <- Super2Users])
  421. ),
  422. ok.
  423. test_authenticator_user(PathPrefix) ->
  424. UsersUri = uri(PathPrefix ++ [?CONF_NS, "password_based:built_in_database", "users"]),
  425. {ok, 200, _} = request(
  426. post,
  427. uri(PathPrefix ++ [?CONF_NS]),
  428. emqx_authn_test_lib:built_in_database_example()
  429. ),
  430. User = #{user_id => <<"u1">>, password => <<"p1">>},
  431. {ok, 201, _} = request(post, UsersUri, User),
  432. {ok, 404, _} = request(get, UsersUri ++ "/u123"),
  433. {ok, 409, _} = request(post, UsersUri, User),
  434. {ok, 200, UserData} = request(get, UsersUri ++ "/u1"),
  435. FetchedUser = emqx_utils_json:decode(UserData, [return_maps]),
  436. ?assertMatch(#{<<"user_id">> := <<"u1">>}, FetchedUser),
  437. ?assertNotMatch(#{<<"password">> := _}, FetchedUser),
  438. ValidUserUpdates = [
  439. #{password => <<"p1">>},
  440. #{password => <<"p1">>, is_superuser => true}
  441. ],
  442. lists:foreach(
  443. fun(UserUpdate) -> {ok, 200, _} = request(put, UsersUri ++ "/u1", UserUpdate) end,
  444. ValidUserUpdates
  445. ),
  446. InvalidUserUpdates = [#{user_id => <<"u1">>, password => <<"p1">>}],
  447. lists:foreach(
  448. fun(UserUpdate) -> {ok, 400, _} = request(put, UsersUri ++ "/u1", UserUpdate) end,
  449. InvalidUserUpdates
  450. ),
  451. {ok, 404, _} = request(delete, UsersUri ++ "/u123"),
  452. {ok, 204, _} = request(delete, UsersUri ++ "/u1").
  453. test_authenticator_position(PathPrefix) ->
  454. AuthenticatorConfs = [
  455. emqx_authn_test_lib:http_example(),
  456. emqx_authn_test_lib:jwt_example(),
  457. emqx_authn_test_lib:built_in_database_example()
  458. ],
  459. lists:foreach(
  460. fun(Conf) ->
  461. {ok, 200, _} = request(
  462. post,
  463. uri(PathPrefix ++ [?CONF_NS]),
  464. Conf
  465. )
  466. end,
  467. AuthenticatorConfs
  468. ),
  469. ?assertAuthenticatorsMatch(
  470. [
  471. #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>},
  472. #{<<"mechanism">> := <<"jwt">>},
  473. #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>}
  474. ],
  475. PathPrefix ++ [?CONF_NS]
  476. ),
  477. %% Invalid moves
  478. {ok, 400, _} = request(
  479. put,
  480. uri(PathPrefix ++ [?CONF_NS, "jwt", "position", "up"])
  481. ),
  482. {ok, 404, _} = request(
  483. put,
  484. uri(PathPrefix ++ [?CONF_NS, "jwt", "position"])
  485. ),
  486. {ok, 404, _} = request(
  487. put,
  488. uri(PathPrefix ++ [?CONF_NS, "jwt", "position", "before:invalid"])
  489. ),
  490. {ok, 404, _} = request(
  491. put,
  492. uri(PathPrefix ++ [?CONF_NS, "jwt", "position", "before:password_based:redis"])
  493. ),
  494. %% Valid moves
  495. %% test front
  496. {ok, 204, _} = request(
  497. put,
  498. uri(PathPrefix ++ [?CONF_NS, "jwt", "position", "front"])
  499. ),
  500. ?assertAuthenticatorsMatch(
  501. [
  502. #{<<"mechanism">> := <<"jwt">>},
  503. #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>},
  504. #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>}
  505. ],
  506. PathPrefix ++ [?CONF_NS]
  507. ),
  508. %% test rear
  509. {ok, 204, _} = request(
  510. put,
  511. uri(PathPrefix ++ [?CONF_NS, "jwt", "position", "rear"])
  512. ),
  513. ?assertAuthenticatorsMatch(
  514. [
  515. #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>},
  516. #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>},
  517. #{<<"mechanism">> := <<"jwt">>}
  518. ],
  519. PathPrefix ++ [?CONF_NS]
  520. ),
  521. %% test before
  522. {ok, 204, _} = request(
  523. put,
  524. uri(PathPrefix ++ [?CONF_NS, "jwt", "position", "before:password_based:built_in_database"])
  525. ),
  526. ?assertAuthenticatorsMatch(
  527. [
  528. #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>},
  529. #{<<"mechanism">> := <<"jwt">>},
  530. #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>}
  531. ],
  532. PathPrefix ++ [?CONF_NS]
  533. ),
  534. %% test after
  535. {ok, 204, _} = request(
  536. put,
  537. uri(
  538. PathPrefix ++
  539. [
  540. ?CONF_NS,
  541. "password_based%3Abuilt_in_database",
  542. "position",
  543. "after:password_based:http"
  544. ]
  545. )
  546. ),
  547. ?assertAuthenticatorsMatch(
  548. [
  549. #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>},
  550. #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>},
  551. #{<<"mechanism">> := <<"jwt">>}
  552. ],
  553. PathPrefix ++ [?CONF_NS]
  554. ).
  555. test_authenticator_import_users(PathPrefix) ->
  556. ImportUri = uri(
  557. PathPrefix ++
  558. [?CONF_NS, "password_based:built_in_database", "import_users"]
  559. ),
  560. {ok, 200, _} = request(
  561. post,
  562. uri(PathPrefix ++ [?CONF_NS]),
  563. emqx_authn_test_lib:built_in_database_example()
  564. ),
  565. {ok, 400, _} = multipart_formdata_request(ImportUri, [], []),
  566. {ok, 400, _} = multipart_formdata_request(ImportUri, [], [
  567. {filenam, "user-credentials.json", <<>>}
  568. ]),
  569. Dir = code:lib_dir(emqx_authn, test),
  570. JSONFileName = filename:join([Dir, <<"data/user-credentials.json">>]),
  571. CSVFileName = filename:join([Dir, <<"data/user-credentials.csv">>]),
  572. {ok, JSONData} = file:read_file(JSONFileName),
  573. {ok, 204, _} = multipart_formdata_request(ImportUri, [], [
  574. {filename, "user-credentials.json", JSONData}
  575. ]),
  576. {ok, CSVData} = file:read_file(CSVFileName),
  577. {ok, 204, _} = multipart_formdata_request(ImportUri, [], [
  578. {filename, "user-credentials.csv", CSVData}
  579. ]).
  580. %% listener authn api is not supported since 5.1.0
  581. %% Don't support listener switch to global chain.
  582. ignore_switch_to_global_chain(_) ->
  583. {ok, 200, _} = request(
  584. post,
  585. uri([?CONF_NS]),
  586. emqx_authn_test_lib:built_in_database_example()
  587. ),
  588. {ok, 200, _} = request(
  589. post,
  590. uri([listeners, "tcp:default", ?CONF_NS]),
  591. emqx_authn_test_lib:built_in_database_example()
  592. ),
  593. {ok, 200, _} = request(
  594. post,
  595. uri([listeners, "tcp:default", ?CONF_NS]),
  596. maps:put(enable, false, emqx_authn_test_lib:http_example())
  597. ),
  598. GlobalUser = #{user_id => <<"global_user">>, password => <<"p1">>},
  599. {ok, 201, _} = request(
  600. post,
  601. uri([?CONF_NS, "password_based:built_in_database", "users"]),
  602. GlobalUser
  603. ),
  604. ListenerUser = #{user_id => <<"listener_user">>, password => <<"p1">>},
  605. {ok, 201, _} = request(
  606. post,
  607. uri([listeners, "tcp:default", ?CONF_NS, "password_based:built_in_database", "users"]),
  608. ListenerUser
  609. ),
  610. process_flag(trap_exit, true),
  611. %% Listener user should be OK
  612. {ok, Client0} = emqtt:start_link([
  613. {username, <<"listener_user">>},
  614. {password, <<"p1">>}
  615. ]),
  616. ?assertMatch(
  617. {ok, _},
  618. emqtt:connect(Client0)
  619. ),
  620. ok = emqtt:disconnect(Client0),
  621. %% Global user should not be OK
  622. {ok, Client1} = emqtt:start_link([
  623. {username, <<"global_user">>},
  624. {password, <<"p1">>}
  625. ]),
  626. ?assertMatch(
  627. {error, {unauthorized_client, _}},
  628. emqtt:connect(Client1)
  629. ),
  630. {ok, 204, _} = request(
  631. delete,
  632. uri([listeners, "tcp:default", ?CONF_NS, "password_based:built_in_database"])
  633. ),
  634. %% Now listener has only disabled authenticators, should allow anonymous access
  635. {ok, Client2} = emqtt:start_link([
  636. {username, <<"any_user">>},
  637. {password, <<"any_password">>}
  638. ]),
  639. ?assertMatch(
  640. {ok, _},
  641. emqtt:connect(Client2)
  642. ),
  643. ok = emqtt:disconnect(Client2),
  644. {ok, 204, _} = request(
  645. delete,
  646. uri([listeners, "tcp:default", ?CONF_NS, "password_based:http"])
  647. ),
  648. %% Local chain is empty now and should be removed
  649. %% Listener user should not be OK
  650. {ok, Client3} = emqtt:start_link([
  651. {username, <<"listener_user">>},
  652. {password, <<"p1">>}
  653. ]),
  654. ?assertMatch(
  655. {error, {unauthorized_client, _}},
  656. emqtt:connect(Client3)
  657. ),
  658. %% Global user should be now OK, switched back to the global chain
  659. {ok, Client4} = emqtt:start_link([
  660. {username, <<"global_user">>},
  661. {password, <<"p1">>}
  662. ]),
  663. ?assertMatch(
  664. {ok, _},
  665. emqtt:connect(Client4)
  666. ),
  667. ok = emqtt:disconnect(Client4).
  668. %%------------------------------------------------------------------------------
  669. %% Helpers
  670. %%------------------------------------------------------------------------------
  671. request(Method, Url) ->
  672. request(Method, Url, []).