emqx_authz_mongodb_SUITE.erl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2020-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. %% 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_authz_mongodb_SUITE).
  16. -compile(nowarn_export_all).
  17. -compile(export_all).
  18. -include_lib("emqx_auth/include/emqx_authz.hrl").
  19. -include_lib("emqx_connector/include/emqx_connector.hrl").
  20. -include_lib("eunit/include/eunit.hrl").
  21. -include_lib("common_test/include/ct.hrl").
  22. -include_lib("emqx/include/emqx_placeholder.hrl").
  23. -define(MONGO_HOST, "mongo").
  24. -define(MONGO_CLIENT, 'emqx_authz_mongo_SUITE_client').
  25. all() ->
  26. emqx_authz_test_lib:all_with_table_case(?MODULE, t_run_case, cases()).
  27. groups() ->
  28. emqx_authz_test_lib:table_groups(t_run_case, cases()).
  29. init_per_suite(Config) ->
  30. case emqx_common_test_helpers:is_tcp_server_available(?MONGO_HOST, ?MONGO_DEFAULT_PORT) of
  31. true ->
  32. Apps = emqx_cth_suite:start(
  33. [
  34. emqx,
  35. {emqx_conf,
  36. "authorization.no_match = deny, authorization.cache.enable = false"},
  37. emqx_auth,
  38. emqx_auth_mongodb
  39. ],
  40. #{work_dir => ?config(priv_dir, Config)}
  41. ),
  42. [{suite_apps, Apps} | Config];
  43. false ->
  44. {skip, no_mongo}
  45. end.
  46. end_per_suite(Config) ->
  47. ok = emqx_authz_test_lib:restore_authorizers(),
  48. emqx_cth_suite:stop(?config(suite_apps, Config)).
  49. init_per_group(Group, Config) ->
  50. [{test_case, emqx_authz_test_lib:get_case(Group, cases())} | Config].
  51. end_per_group(_Group, _Config) ->
  52. ok.
  53. init_per_testcase(_TestCase, Config) ->
  54. {ok, _} = mc_worker_api:connect(mongo_config()),
  55. ok = emqx_authz_test_lib:reset_authorizers(),
  56. Config.
  57. end_per_testcase(_TestCase, _Config) ->
  58. _ = emqx_authz:set_feature_available(rich_actions, true),
  59. ok = reset_samples(),
  60. ok = mc_worker_api:disconnect(?MONGO_CLIENT).
  61. %%------------------------------------------------------------------------------
  62. %% Testcases
  63. %%------------------------------------------------------------------------------
  64. t_run_case(Config) ->
  65. Case = ?config(test_case, Config),
  66. ok = setup_source_data(Case),
  67. ok = setup_authz_source(Case),
  68. ok = emqx_authz_test_lib:run_checks(Case).
  69. %%------------------------------------------------------------------------------
  70. %% Cases
  71. %%------------------------------------------------------------------------------
  72. cases() ->
  73. [
  74. #{
  75. name => base_publish,
  76. records => [
  77. #{
  78. <<"username">> => <<"username">>,
  79. <<"action">> => <<"publish">>,
  80. <<"topic">> => <<"a">>,
  81. <<"permission">> => <<"allow">>
  82. },
  83. #{
  84. <<"username">> => <<"username">>,
  85. <<"action">> => <<"subscribe">>,
  86. <<"topic">> => <<"b">>,
  87. <<"permission">> => <<"allow">>
  88. },
  89. #{
  90. <<"username">> => <<"username">>,
  91. <<"action">> => <<"all">>,
  92. <<"topics">> => [<<"c">>, <<"d">>],
  93. <<"permission">> => <<"allow">>
  94. }
  95. ],
  96. filter => #{<<"username">> => <<"${username}">>},
  97. checks => [
  98. {allow, ?AUTHZ_PUBLISH, <<"a">>},
  99. {deny, ?AUTHZ_SUBSCRIBE, <<"a">>},
  100. {deny, ?AUTHZ_PUBLISH, <<"b">>},
  101. {allow, ?AUTHZ_SUBSCRIBE, <<"b">>},
  102. {allow, ?AUTHZ_PUBLISH, <<"c">>},
  103. {allow, ?AUTHZ_SUBSCRIBE, <<"c">>},
  104. {allow, ?AUTHZ_PUBLISH, <<"d">>},
  105. {allow, ?AUTHZ_SUBSCRIBE, <<"d">>}
  106. ]
  107. },
  108. #{
  109. name => filter_works,
  110. records => [
  111. #{
  112. <<"action">> => <<"publish">>,
  113. <<"topic">> => <<"a">>,
  114. <<"permission">> => <<"allow">>
  115. }
  116. ],
  117. filter => #{<<"username">> => <<"${username}">>},
  118. checks => [
  119. {deny, ?AUTHZ_PUBLISH, <<"a">>}
  120. ]
  121. },
  122. #{
  123. name => invalid_rich_rules,
  124. features => [rich_actions],
  125. records => [
  126. #{
  127. <<"action">> => <<"publish">>,
  128. <<"topic">> => <<"a">>,
  129. <<"permission">> => <<"allow">>,
  130. <<"qos">> => <<"1,2,3">>
  131. },
  132. #{
  133. <<"action">> => <<"publish">>,
  134. <<"topic">> => <<"a">>,
  135. <<"permission">> => <<"allow">>,
  136. <<"retain">> => <<"yes">>
  137. }
  138. ],
  139. filter => #{},
  140. checks => [
  141. {deny, ?AUTHZ_PUBLISH, <<"a">>}
  142. ]
  143. },
  144. #{
  145. name => invalid_rules,
  146. records => [
  147. #{
  148. <<"action">> => <<"publis">>,
  149. <<"topic">> => <<"a">>,
  150. <<"permission">> => <<"allow">>
  151. }
  152. ],
  153. filter => #{},
  154. checks => [
  155. {deny, ?AUTHZ_PUBLISH, <<"a">>}
  156. ]
  157. },
  158. #{
  159. name => rule_by_clientid_cn_dn_peerhost,
  160. records => [
  161. #{
  162. <<"cn">> => <<"cn">>,
  163. <<"dn">> => <<"dn">>,
  164. <<"clientid">> => <<"clientid">>,
  165. <<"peerhost">> => <<"127.0.0.1">>,
  166. <<"action">> => <<"publish">>,
  167. <<"topic">> => <<"a">>,
  168. <<"permission">> => <<"allow">>
  169. }
  170. ],
  171. client_info => #{
  172. cn => <<"cn">>,
  173. dn => <<"dn">>
  174. },
  175. filter => #{
  176. <<"cn">> => <<"${cert_common_name}">>,
  177. <<"dn">> => <<"${cert_subject}">>,
  178. <<"clientid">> => <<"${clientid}">>,
  179. <<"peerhost">> => <<"${peerhost}">>
  180. },
  181. checks => [
  182. {allow, ?AUTHZ_PUBLISH, <<"a">>}
  183. ]
  184. },
  185. #{
  186. name => topics_literal_wildcard_variable,
  187. records => [
  188. #{
  189. <<"username">> => <<"username">>,
  190. <<"action">> => <<"publish">>,
  191. <<"permission">> => <<"allow">>,
  192. <<"topics">> => [
  193. <<"t/${username}">>,
  194. <<"t/${clientid}">>,
  195. <<"t1/#">>,
  196. <<"t2/+">>,
  197. <<"eq t3/${username}">>
  198. ]
  199. }
  200. ],
  201. filter => #{<<"username">> => <<"${username}">>},
  202. checks => [
  203. {allow, ?AUTHZ_PUBLISH, <<"t/username">>},
  204. {allow, ?AUTHZ_PUBLISH, <<"t/clientid">>},
  205. {allow, ?AUTHZ_PUBLISH, <<"t1/a/b">>},
  206. {allow, ?AUTHZ_PUBLISH, <<"t2/a">>},
  207. {allow, ?AUTHZ_PUBLISH, <<"t3/${username}">>},
  208. {deny, ?AUTHZ_PUBLISH, <<"t3/username">>}
  209. ]
  210. },
  211. #{
  212. name => qos_retain_in_query_result,
  213. features => [rich_actions],
  214. records => [
  215. #{
  216. <<"username">> => <<"username">>,
  217. <<"action">> => <<"publish">>,
  218. <<"permission">> => <<"allow">>,
  219. <<"topic">> => <<"a">>,
  220. <<"qos">> => 1,
  221. <<"retain">> => true
  222. },
  223. #{
  224. <<"username">> => <<"username">>,
  225. <<"action">> => <<"publish">>,
  226. <<"permission">> => <<"allow">>,
  227. <<"topic">> => <<"b">>,
  228. <<"qos">> => <<"1">>,
  229. <<"retain">> => <<"true">>
  230. },
  231. #{
  232. <<"username">> => <<"username">>,
  233. <<"action">> => <<"publish">>,
  234. <<"permission">> => <<"allow">>,
  235. <<"topic">> => <<"c">>,
  236. <<"qos">> => <<"1,2">>,
  237. <<"retain">> => 1
  238. },
  239. #{
  240. <<"username">> => <<"username">>,
  241. <<"action">> => <<"publish">>,
  242. <<"permission">> => <<"allow">>,
  243. <<"topic">> => <<"d">>,
  244. <<"qos">> => [1, 2],
  245. <<"retain">> => <<"1">>
  246. },
  247. #{
  248. <<"username">> => <<"username">>,
  249. <<"action">> => <<"publish">>,
  250. <<"permission">> => <<"allow">>,
  251. <<"topic">> => <<"e">>,
  252. <<"qos">> => [1, 2],
  253. <<"retain">> => <<"all">>
  254. },
  255. #{
  256. <<"username">> => <<"username">>,
  257. <<"action">> => <<"publish">>,
  258. <<"permission">> => <<"allow">>,
  259. <<"topic">> => <<"f">>,
  260. <<"qos">> => null,
  261. <<"retain">> => null
  262. }
  263. ],
  264. filter => #{<<"username">> => <<"${username}">>},
  265. checks => [
  266. {allow, ?AUTHZ_PUBLISH(1, true), <<"a">>},
  267. {deny, ?AUTHZ_PUBLISH(1, false), <<"a">>},
  268. {allow, ?AUTHZ_PUBLISH(1, true), <<"b">>},
  269. {deny, ?AUTHZ_PUBLISH(1, false), <<"b">>},
  270. {deny, ?AUTHZ_PUBLISH(2, false), <<"b">>},
  271. {allow, ?AUTHZ_PUBLISH(2, true), <<"c">>},
  272. {deny, ?AUTHZ_PUBLISH(2, false), <<"c">>},
  273. {deny, ?AUTHZ_PUBLISH(0, true), <<"c">>},
  274. {allow, ?AUTHZ_PUBLISH(2, true), <<"d">>},
  275. {deny, ?AUTHZ_PUBLISH(0, true), <<"d">>},
  276. {allow, ?AUTHZ_PUBLISH(1, false), <<"e">>},
  277. {allow, ?AUTHZ_PUBLISH(1, true), <<"e">>},
  278. {deny, ?AUTHZ_PUBLISH(0, false), <<"e">>},
  279. {allow, ?AUTHZ_PUBLISH, <<"f">>},
  280. {deny, ?AUTHZ_SUBSCRIBE, <<"f">>}
  281. ]
  282. },
  283. #{
  284. name => nonbin_values_in_client_info,
  285. records => [
  286. #{
  287. <<"username">> => <<"username">>,
  288. <<"clientid">> => <<"clientid">>,
  289. <<"action">> => <<"publish">>,
  290. <<"topic">> => <<"a">>,
  291. <<"permission">> => <<"allow">>
  292. }
  293. ],
  294. client_info => #{
  295. username => "username",
  296. clientid => clientid
  297. },
  298. filter => #{<<"username">> => <<"${username}">>, <<"clientid">> => <<"${clientid}">>},
  299. checks => [
  300. {allow, ?AUTHZ_PUBLISH, <<"a">>}
  301. ]
  302. },
  303. #{
  304. name => invalid_query,
  305. records => [
  306. #{
  307. <<"action">> => <<"publish">>,
  308. <<"topic">> => <<"a">>,
  309. <<"permission">> => <<"allow">>
  310. }
  311. ],
  312. filter => #{<<"$in">> => #{<<"a">> => 1}},
  313. checks => [
  314. {deny, ?AUTHZ_PUBLISH, <<"a">>}
  315. ]
  316. },
  317. #{
  318. name => complex_query,
  319. records => [
  320. #{
  321. <<"a">> => #{<<"u">> => <<"clientid">>, <<"c">> => [<<"cn">>, <<"dn">>]},
  322. <<"action">> => <<"publish">>,
  323. <<"topic">> => <<"a">>,
  324. <<"permission">> => <<"allow">>
  325. }
  326. ],
  327. client_info => #{
  328. cn => <<"cn">>,
  329. dn => <<"dn">>
  330. },
  331. filter => #{
  332. <<"a">> => #{
  333. <<"u">> => <<"${clientid}">>,
  334. <<"c">> => [<<"${cert_common_name}">>, <<"${cert_subject}">>]
  335. }
  336. },
  337. checks => [
  338. {allow, ?AUTHZ_PUBLISH, <<"a">>}
  339. ]
  340. }
  341. ].
  342. %%------------------------------------------------------------------------------
  343. %% Helpers
  344. %%------------------------------------------------------------------------------
  345. reset_samples() ->
  346. {true, _} = mc_worker_api:delete(?MONGO_CLIENT, <<"acl">>, #{}),
  347. ok.
  348. setup_source_data(#{records := Records}) ->
  349. {{true, _}, _} = mc_worker_api:insert(?MONGO_CLIENT, <<"acl">>, Records),
  350. ok.
  351. setup_authz_source(#{filter := Filter}) ->
  352. setup_config(
  353. #{
  354. <<"filter">> => Filter
  355. }
  356. ).
  357. setup_config(SpecialParams) ->
  358. emqx_authz_test_lib:setup_config(
  359. raw_mongo_authz_config(),
  360. SpecialParams
  361. ).
  362. raw_mongo_authz_config() ->
  363. #{
  364. <<"type">> => <<"mongodb">>,
  365. <<"enable">> => <<"true">>,
  366. <<"mongo_type">> => <<"single">>,
  367. <<"database">> => <<"mqtt">>,
  368. <<"collection">> => <<"acl">>,
  369. <<"server">> => mongo_server(),
  370. <<"auth_source">> => mongo_authsource(),
  371. <<"username">> => mongo_username(),
  372. <<"password">> => mongo_password(),
  373. <<"filter">> => #{<<"username">> => <<"${username}">>}
  374. }.
  375. mongo_server() ->
  376. iolist_to_binary(io_lib:format("~s", [?MONGO_HOST])).
  377. mongo_config() ->
  378. [
  379. {database, <<"mqtt">>},
  380. {host, ?MONGO_HOST},
  381. {port, ?MONGO_DEFAULT_PORT},
  382. {auth_source, mongo_authsource()},
  383. {login, mongo_username()},
  384. {password, mongo_password()},
  385. {register, ?MONGO_CLIENT}
  386. ].
  387. mongo_authsource() ->
  388. iolist_to_binary(os:getenv("MONGO_AUTHSOURCE", "admin")).
  389. mongo_username() ->
  390. iolist_to_binary(os:getenv("MONGO_USERNAME", "")).
  391. mongo_password() ->
  392. iolist_to_binary(os:getenv("MONGO_PASSWORD", "")).
  393. start_apps(Apps) ->
  394. lists:foreach(fun application:ensure_all_started/1, Apps).
  395. stop_apps(Apps) ->
  396. lists:foreach(fun application:stop/1, Apps).