emqx_authz_api_mnesia.erl 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  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. %%
  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_authz_api_mnesia).
  17. -behaviour(minirest_api).
  18. -include("emqx_auth_mnesia.hrl").
  19. -include_lib("emqx_auth/include/emqx_authz.hrl").
  20. -include_lib("emqx/include/logger.hrl").
  21. -include_lib("hocon/include/hoconsc.hrl").
  22. -import(hoconsc, [mk/1, mk/2, ref/1, ref/2, array/1, enum/1]).
  23. -define(QUERY_USERNAME_FUN, fun ?MODULE:query_username/2).
  24. -define(QUERY_CLIENTID_FUN, fun ?MODULE:query_clientid/2).
  25. -define(ACL_USERNAME_QSCHEMA, [{<<"like_username">>, binary}]).
  26. -define(ACL_CLIENTID_QSCHEMA, [{<<"like_clientid">>, binary}]).
  27. -export([
  28. api_spec/0,
  29. paths/0,
  30. schema/1,
  31. fields/1,
  32. namespace/0
  33. ]).
  34. %% operation funs
  35. -export([
  36. users/2,
  37. clients/2,
  38. user/2,
  39. client/2,
  40. all/2,
  41. rules/2
  42. ]).
  43. %% query funs
  44. -export([
  45. query_username/2,
  46. query_clientid/2,
  47. run_fuzzy_filter/2,
  48. format_result/1
  49. ]).
  50. %% minirest filter callback
  51. -export([is_configured_authz_source/2]).
  52. -define(BAD_REQUEST, 'BAD_REQUEST').
  53. -define(NOT_FOUND, 'NOT_FOUND').
  54. -define(ALREADY_EXISTS, 'ALREADY_EXISTS').
  55. -define(TYPE_REF, ref).
  56. -define(TYPE_ARRAY, array).
  57. -define(PAGE_QUERY_EXAMPLE, example_in_data).
  58. -define(PUT_MAP_EXAMPLE, in_put_requestBody).
  59. -define(POST_ARRAY_EXAMPLE, in_post_requestBody).
  60. namespace() -> undefined.
  61. api_spec() ->
  62. emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
  63. paths() ->
  64. [
  65. "/authorization/sources/built_in_database/rules/users",
  66. "/authorization/sources/built_in_database/rules/clients",
  67. "/authorization/sources/built_in_database/rules/users/:username",
  68. "/authorization/sources/built_in_database/rules/clients/:clientid",
  69. "/authorization/sources/built_in_database/rules/all",
  70. "/authorization/sources/built_in_database/rules"
  71. ].
  72. %%--------------------------------------------------------------------
  73. %% Schema for each URI
  74. %%--------------------------------------------------------------------
  75. schema("/authorization/sources/built_in_database/rules/users") ->
  76. #{
  77. 'operationId' => users,
  78. filter => fun ?MODULE:is_configured_authz_source/2,
  79. get =>
  80. #{
  81. tags => [<<"authorization">>],
  82. description => ?DESC(users_username_get),
  83. parameters =>
  84. [
  85. ref(emqx_dashboard_swagger, page),
  86. ref(emqx_dashboard_swagger, limit),
  87. {like_username,
  88. mk(binary(), #{
  89. in => query,
  90. required => false,
  91. desc => ?DESC(fuzzy_username)
  92. })}
  93. ],
  94. responses =>
  95. #{
  96. 200 => swagger_with_example(
  97. {username_response_data, ?TYPE_REF},
  98. {username, ?PAGE_QUERY_EXAMPLE}
  99. )
  100. }
  101. },
  102. post =>
  103. #{
  104. tags => [<<"authorization">>],
  105. description => ?DESC(users_username_post),
  106. 'requestBody' => swagger_with_example(
  107. {rules_for_username, ?TYPE_ARRAY},
  108. {username, ?POST_ARRAY_EXAMPLE}
  109. ),
  110. responses =>
  111. #{
  112. 204 => <<"Created">>,
  113. 400 => emqx_dashboard_swagger:error_codes(
  114. [?BAD_REQUEST], <<"Bad username or bad rule schema">>
  115. ),
  116. 409 => emqx_dashboard_swagger:error_codes(
  117. [?ALREADY_EXISTS], <<"ALREADY_EXISTS">>
  118. )
  119. }
  120. }
  121. };
  122. schema("/authorization/sources/built_in_database/rules/clients") ->
  123. #{
  124. 'operationId' => clients,
  125. filter => fun ?MODULE:is_configured_authz_source/2,
  126. get =>
  127. #{
  128. tags => [<<"authorization">>],
  129. description => ?DESC(users_clientid_get),
  130. parameters =>
  131. [
  132. ref(emqx_dashboard_swagger, page),
  133. ref(emqx_dashboard_swagger, limit),
  134. {like_clientid,
  135. mk(
  136. binary(),
  137. #{
  138. in => query,
  139. required => false,
  140. desc => ?DESC(fuzzy_clientid)
  141. }
  142. )}
  143. ],
  144. responses =>
  145. #{
  146. 200 => swagger_with_example(
  147. {clientid_response_data, ?TYPE_REF},
  148. {clientid, ?PAGE_QUERY_EXAMPLE}
  149. )
  150. }
  151. },
  152. post =>
  153. #{
  154. tags => [<<"authorization">>],
  155. description => ?DESC(users_clientid_post),
  156. 'requestBody' => swagger_with_example(
  157. {rules_for_clientid, ?TYPE_ARRAY},
  158. {clientid, ?POST_ARRAY_EXAMPLE}
  159. ),
  160. responses =>
  161. #{
  162. 204 => <<"Created">>,
  163. 400 => emqx_dashboard_swagger:error_codes(
  164. [?BAD_REQUEST], <<"Bad clientid or bad rule schema">>
  165. )
  166. }
  167. }
  168. };
  169. schema("/authorization/sources/built_in_database/rules/users/:username") ->
  170. #{
  171. 'operationId' => user,
  172. filter => fun ?MODULE:is_configured_authz_source/2,
  173. get =>
  174. #{
  175. tags => [<<"authorization">>],
  176. description => ?DESC(user_username_get),
  177. parameters => [ref(username)],
  178. responses =>
  179. #{
  180. 200 => swagger_with_example(
  181. {rules_for_username, ?TYPE_REF},
  182. {username, ?PUT_MAP_EXAMPLE}
  183. ),
  184. 404 => emqx_dashboard_swagger:error_codes(
  185. [?NOT_FOUND], <<"Not Found">>
  186. )
  187. }
  188. },
  189. put =>
  190. #{
  191. tags => [<<"authorization">>],
  192. description => ?DESC(user_username_put),
  193. parameters => [ref(username)],
  194. 'requestBody' => swagger_with_example(
  195. {rules_for_username, ?TYPE_REF},
  196. {username, ?PUT_MAP_EXAMPLE}
  197. ),
  198. responses =>
  199. #{
  200. 204 => <<"Updated">>,
  201. 400 => emqx_dashboard_swagger:error_codes(
  202. [?BAD_REQUEST], <<"Bad username or bad rule schema">>
  203. )
  204. }
  205. },
  206. delete =>
  207. #{
  208. tags => [<<"authorization">>],
  209. description => ?DESC(user_username_delete),
  210. parameters => [ref(username)],
  211. responses =>
  212. #{
  213. 204 => <<"Deleted">>,
  214. 400 => emqx_dashboard_swagger:error_codes(
  215. [?BAD_REQUEST], <<"Bad username">>
  216. ),
  217. 404 => emqx_dashboard_swagger:error_codes(
  218. [?NOT_FOUND], <<"Username Not Found">>
  219. )
  220. }
  221. }
  222. };
  223. schema("/authorization/sources/built_in_database/rules/clients/:clientid") ->
  224. #{
  225. 'operationId' => client,
  226. filter => fun ?MODULE:is_configured_authz_source/2,
  227. get =>
  228. #{
  229. tags => [<<"authorization">>],
  230. description => ?DESC(user_clientid_get),
  231. parameters => [ref(clientid)],
  232. responses =>
  233. #{
  234. 200 => swagger_with_example(
  235. {rules_for_clientid, ?TYPE_REF},
  236. {clientid, ?PUT_MAP_EXAMPLE}
  237. ),
  238. 404 => emqx_dashboard_swagger:error_codes(
  239. [?NOT_FOUND], <<"Not Found">>
  240. )
  241. }
  242. },
  243. put =>
  244. #{
  245. tags => [<<"authorization">>],
  246. description => ?DESC(user_clientid_put),
  247. parameters => [ref(clientid)],
  248. 'requestBody' => swagger_with_example(
  249. {rules_for_clientid, ?TYPE_REF},
  250. {clientid, ?PUT_MAP_EXAMPLE}
  251. ),
  252. responses =>
  253. #{
  254. 204 => <<"Updated">>,
  255. 400 => emqx_dashboard_swagger:error_codes(
  256. [?BAD_REQUEST], <<"Bad clientid or bad rule schema">>
  257. )
  258. }
  259. },
  260. delete =>
  261. #{
  262. tags => [<<"authorization">>],
  263. description => ?DESC(user_clientid_delete),
  264. parameters => [ref(clientid)],
  265. responses =>
  266. #{
  267. 204 => <<"Deleted">>,
  268. 400 => emqx_dashboard_swagger:error_codes(
  269. [?BAD_REQUEST], <<"Bad clientid">>
  270. ),
  271. 404 => emqx_dashboard_swagger:error_codes(
  272. [?NOT_FOUND], <<"ClientID Not Found">>
  273. )
  274. }
  275. }
  276. };
  277. schema("/authorization/sources/built_in_database/rules/all") ->
  278. #{
  279. 'operationId' => all,
  280. filter => fun ?MODULE:is_configured_authz_source/2,
  281. get =>
  282. #{
  283. tags => [<<"authorization">>],
  284. description => ?DESC(rules_all_get),
  285. responses =>
  286. #{200 => swagger_with_example({rules, ?TYPE_REF}, {all, ?PUT_MAP_EXAMPLE})}
  287. },
  288. post =>
  289. #{
  290. tags => [<<"authorization">>],
  291. description => ?DESC(rules_all_post),
  292. 'requestBody' =>
  293. swagger_with_example({rules, ?TYPE_REF}, {all, ?PUT_MAP_EXAMPLE}),
  294. responses =>
  295. #{
  296. 204 => <<"Updated">>,
  297. 400 => emqx_dashboard_swagger:error_codes(
  298. [?BAD_REQUEST], <<"Bad rule schema">>
  299. )
  300. }
  301. },
  302. delete =>
  303. #{
  304. tags => [<<"authorization">>],
  305. description => ?DESC(rules_all_delete),
  306. responses =>
  307. #{
  308. 204 => <<"Deleted">>
  309. }
  310. }
  311. };
  312. schema("/authorization/sources/built_in_database/rules") ->
  313. #{
  314. 'operationId' => rules,
  315. filter => fun ?MODULE:is_configured_authz_source/2,
  316. delete =>
  317. #{
  318. tags => [<<"authorization">>],
  319. description => ?DESC(rules_delete),
  320. responses =>
  321. #{
  322. 204 => <<"Deleted">>,
  323. 400 => emqx_dashboard_swagger:error_codes(
  324. [?BAD_REQUEST], <<"Bad Request">>
  325. )
  326. }
  327. }
  328. }.
  329. fields(rule_item) ->
  330. [
  331. {topic,
  332. mk(
  333. string(),
  334. #{
  335. required => true,
  336. desc => ?DESC(topic),
  337. example => <<"test/topic/1">>
  338. }
  339. )},
  340. {permission,
  341. mk(
  342. enum([allow, deny]),
  343. #{
  344. desc => ?DESC(permission),
  345. required => true,
  346. example => allow
  347. }
  348. )},
  349. {action,
  350. mk(
  351. enum([publish, subscribe, all]),
  352. #{
  353. desc => ?DESC(action),
  354. required => true,
  355. example => publish
  356. }
  357. )},
  358. {qos,
  359. mk(
  360. array(emqx_schema:qos()),
  361. #{
  362. desc => ?DESC(qos),
  363. default => ?DEFAULT_RULE_QOS
  364. }
  365. )},
  366. {retain,
  367. mk(
  368. hoconsc:union([all, boolean()]),
  369. #{
  370. desc => ?DESC(retain),
  371. default => ?DEFAULT_RULE_RETAIN
  372. }
  373. )}
  374. ];
  375. fields(clientid) ->
  376. [
  377. {clientid,
  378. mk(
  379. binary(),
  380. #{
  381. in => path,
  382. required => true,
  383. desc => ?DESC(clientid),
  384. example => <<"client1">>
  385. }
  386. )}
  387. ];
  388. fields(username) ->
  389. [
  390. {username,
  391. mk(
  392. binary(),
  393. #{
  394. in => path,
  395. required => true,
  396. desc => ?DESC(username),
  397. example => <<"user1">>
  398. }
  399. )}
  400. ];
  401. fields(rules_for_username) ->
  402. fields(rules) ++
  403. fields(username);
  404. fields(username_response_data) ->
  405. [
  406. {data, mk(array(ref(rules_for_username)), #{})},
  407. {meta, ref(emqx_dashboard_swagger, meta)}
  408. ];
  409. fields(rules_for_clientid) ->
  410. fields(rules) ++
  411. fields(clientid);
  412. fields(clientid_response_data) ->
  413. [
  414. {data, mk(array(ref(rules_for_clientid)), #{})},
  415. {meta, ref(emqx_dashboard_swagger, meta)}
  416. ];
  417. fields(rules) ->
  418. [{rules, mk(array(ref(rule_item)))}].
  419. %%--------------------------------------------------------------------
  420. %% HTTP API
  421. %%--------------------------------------------------------------------
  422. is_configured_authz_source(Params, _Meta) ->
  423. emqx_authz_api_sources:with_source(
  424. ?AUTHZ_TYPE_BIN,
  425. fun(_Source) ->
  426. {ok, Params}
  427. end
  428. ).
  429. users(get, #{query_string := QueryString}) ->
  430. case
  431. emqx_mgmt_api:node_query(
  432. node(),
  433. ?ACL_TABLE,
  434. QueryString,
  435. ?ACL_USERNAME_QSCHEMA,
  436. ?QUERY_USERNAME_FUN,
  437. fun ?MODULE:format_result/1
  438. )
  439. of
  440. {error, page_limit_invalid} ->
  441. {400, #{code => <<"INVALID_PARAMETER">>, message => <<"page_limit_invalid">>}};
  442. {error, Node, Error} ->
  443. Message = list_to_binary(
  444. io_lib:format("bad rpc call ~p, Reason ~p", [Node, Error])
  445. ),
  446. {500, #{code => <<"NODE_DOWN">>, message => Message}};
  447. Result ->
  448. {200, Result}
  449. end;
  450. users(post, #{body := Body}) when is_list(Body) ->
  451. case ensure_all_not_exists(<<"username">>, username, Body) of
  452. [] ->
  453. lists:foreach(
  454. fun(#{<<"username">> := Username, <<"rules">> := Rules}) ->
  455. emqx_authz_mnesia:store_rules({username, Username}, Rules)
  456. end,
  457. Body
  458. ),
  459. {204};
  460. Exists ->
  461. {409, #{
  462. code => <<"ALREADY_EXISTS">>,
  463. message => binfmt("Users '~ts' already exist", [binjoin(Exists)])
  464. }}
  465. end.
  466. clients(get, #{query_string := QueryString}) ->
  467. case
  468. emqx_mgmt_api:node_query(
  469. node(),
  470. ?ACL_TABLE,
  471. QueryString,
  472. ?ACL_CLIENTID_QSCHEMA,
  473. ?QUERY_CLIENTID_FUN,
  474. fun ?MODULE:format_result/1
  475. )
  476. of
  477. {error, page_limit_invalid} ->
  478. {400, #{code => <<"INVALID_PARAMETER">>, message => <<"page_limit_invalid">>}};
  479. {error, Node, Error} ->
  480. Message = list_to_binary(
  481. io_lib:format("bad rpc call ~p, Reason ~p", [Node, Error])
  482. ),
  483. {500, #{code => <<"NODE_DOWN">>, message => Message}};
  484. Result ->
  485. {200, Result}
  486. end;
  487. clients(post, #{body := Body}) when is_list(Body) ->
  488. case ensure_all_not_exists(<<"clientid">>, clientid, Body) of
  489. [] ->
  490. lists:foreach(
  491. fun(#{<<"clientid">> := ClientID, <<"rules">> := Rules}) ->
  492. emqx_authz_mnesia:store_rules({clientid, ClientID}, Rules)
  493. end,
  494. Body
  495. ),
  496. {204};
  497. Exists ->
  498. {409, #{
  499. code => <<"ALREADY_EXISTS">>,
  500. message => binfmt("Clients '~ts' already exist", [binjoin(Exists)])
  501. }}
  502. end.
  503. user(get, #{bindings := #{username := Username}}) ->
  504. case emqx_authz_mnesia:get_rules({username, Username}) of
  505. not_found ->
  506. {404, #{code => <<"NOT_FOUND">>, message => <<"Not Found">>}};
  507. {ok, Rules} ->
  508. {200, #{
  509. username => Username,
  510. rules => format_rules(Rules)
  511. }}
  512. end;
  513. user(put, #{
  514. bindings := #{username := Username},
  515. body := #{<<"username">> := Username, <<"rules">> := Rules}
  516. }) ->
  517. emqx_authz_mnesia:store_rules({username, Username}, Rules),
  518. {204};
  519. user(delete, #{bindings := #{username := Username}}) ->
  520. case emqx_authz_mnesia:get_rules({username, Username}) of
  521. not_found ->
  522. {404, #{code => <<"NOT_FOUND">>, message => <<"Username Not Found">>}};
  523. {ok, _Rules} ->
  524. emqx_authz_mnesia:delete_rules({username, Username}),
  525. {204}
  526. end.
  527. client(get, #{bindings := #{clientid := ClientID}}) ->
  528. case emqx_authz_mnesia:get_rules({clientid, ClientID}) of
  529. not_found ->
  530. {404, #{code => <<"NOT_FOUND">>, message => <<"Not Found">>}};
  531. {ok, Rules} ->
  532. {200, #{
  533. clientid => ClientID,
  534. rules => format_rules(Rules)
  535. }}
  536. end;
  537. client(put, #{
  538. bindings := #{clientid := ClientID},
  539. body := #{<<"clientid">> := ClientID, <<"rules">> := Rules}
  540. }) ->
  541. emqx_authz_mnesia:store_rules({clientid, ClientID}, Rules),
  542. {204};
  543. client(delete, #{bindings := #{clientid := ClientID}}) ->
  544. case emqx_authz_mnesia:get_rules({clientid, ClientID}) of
  545. not_found ->
  546. {404, #{code => <<"NOT_FOUND">>, message => <<"ClientID Not Found">>}};
  547. {ok, _Rules} ->
  548. emqx_authz_mnesia:delete_rules({clientid, ClientID}),
  549. {204}
  550. end.
  551. all(get, _) ->
  552. case emqx_authz_mnesia:get_rules(all) of
  553. not_found ->
  554. {200, #{rules => []}};
  555. {ok, Rules} ->
  556. {200, #{
  557. rules => format_rules(Rules)
  558. }}
  559. end;
  560. all(post, #{body := #{<<"rules">> := Rules}}) ->
  561. emqx_authz_mnesia:store_rules(all, Rules),
  562. {204};
  563. all(delete, _) ->
  564. emqx_authz_mnesia:store_rules(all, []),
  565. {204}.
  566. rules(delete, _) ->
  567. case emqx_authz_api_sources:get_raw_source(<<"built_in_database">>) of
  568. [#{<<"enable">> := false}] ->
  569. ok = emqx_authz_mnesia:purge_rules(),
  570. {204};
  571. [#{<<"enable">> := true}] ->
  572. {400, #{
  573. code => <<"BAD_REQUEST">>,
  574. message =>
  575. <<"'built_in_database' type source must be disabled before purge.">>
  576. }};
  577. [] ->
  578. {404, #{
  579. code => <<"BAD_REQUEST">>,
  580. message => <<"'built_in_database' type source is not found.">>
  581. }}
  582. end.
  583. %%--------------------------------------------------------------------
  584. %% QueryString to MatchSpec
  585. -spec query_username(atom(), {list(), list()}) -> emqx_mgmt_api:match_spec_and_filter().
  586. query_username(_Tab, {_QString, FuzzyQString}) ->
  587. #{
  588. match_spec => emqx_authz_mnesia:list_username_rules(),
  589. fuzzy_fun => fuzzy_filter_fun(FuzzyQString)
  590. }.
  591. -spec query_clientid(atom(), {list(), list()}) -> emqx_mgmt_api:match_spec_and_filter().
  592. query_clientid(_Tab, {_QString, FuzzyQString}) ->
  593. #{
  594. match_spec => emqx_authz_mnesia:list_clientid_rules(),
  595. fuzzy_fun => fuzzy_filter_fun(FuzzyQString)
  596. }.
  597. %% Fuzzy username funcs
  598. fuzzy_filter_fun([]) ->
  599. undefined;
  600. fuzzy_filter_fun(Fuzzy) ->
  601. {fun ?MODULE:run_fuzzy_filter/2, [Fuzzy]}.
  602. run_fuzzy_filter(_, []) ->
  603. true;
  604. run_fuzzy_filter(
  605. E = [{username, Username}, _Rule],
  606. [{username, like, UsernameSubStr} | Fuzzy]
  607. ) ->
  608. binary:match(Username, UsernameSubStr) /= nomatch andalso run_fuzzy_filter(E, Fuzzy);
  609. run_fuzzy_filter(
  610. E = [{clientid, ClientId}, _Rule],
  611. [{clientid, like, ClientIdSubStr} | Fuzzy]
  612. ) ->
  613. binary:match(ClientId, ClientIdSubStr) /= nomatch andalso run_fuzzy_filter(E, Fuzzy).
  614. %%--------------------------------------------------------------------
  615. %% format funcs
  616. %% format result from mnesia tab
  617. format_result([{username, Username}, {rules, Rules}]) ->
  618. #{
  619. username => Username,
  620. rules => format_rules(Rules)
  621. };
  622. format_result([{clientid, ClientID}, {rules, Rules}]) ->
  623. #{
  624. clientid => ClientID,
  625. rules => format_rules(Rules)
  626. }.
  627. format_rules(Rules) ->
  628. [emqx_authz_rule_raw:format_rule(Rule) || Rule <- Rules].
  629. %%--------------------------------------------------------------------
  630. %% Internal functions
  631. %%--------------------------------------------------------------------
  632. swagger_with_example({Ref, TypeP}, {_Name, _Type} = Example) ->
  633. emqx_dashboard_swagger:schema_with_examples(
  634. case TypeP of
  635. ?TYPE_REF -> ref(?MODULE, Ref);
  636. ?TYPE_ARRAY -> array(ref(?MODULE, Ref))
  637. end,
  638. rules_example(Example)
  639. ).
  640. rules_example({ExampleName, ExampleType}) ->
  641. {Summary, Example} =
  642. case ExampleName of
  643. username -> {<<"Username">>, ?USERNAME_RULES_EXAMPLE};
  644. clientid -> {<<"ClientID">>, ?CLIENTID_RULES_EXAMPLE};
  645. all -> {<<"All">>, ?ALL_RULES_EXAMPLE}
  646. end,
  647. Value =
  648. case ExampleType of
  649. ?PAGE_QUERY_EXAMPLE ->
  650. #{
  651. data => [Example],
  652. meta => ?META_EXAMPLE
  653. };
  654. ?PUT_MAP_EXAMPLE ->
  655. Example;
  656. ?POST_ARRAY_EXAMPLE ->
  657. [Example]
  658. end,
  659. #{
  660. 'password_based:built_in_database' => #{
  661. summary => Summary,
  662. value => Value
  663. }
  664. }.
  665. ensure_all_not_exists(Key, Type, Cfgs) ->
  666. lists:foldl(
  667. fun(#{Key := Id}, Acc) ->
  668. case emqx_authz_mnesia:get_rules({Type, Id}) of
  669. not_found ->
  670. Acc;
  671. _ ->
  672. [Id | Acc]
  673. end
  674. end,
  675. [],
  676. Cfgs
  677. ).
  678. binjoin([Bin]) ->
  679. Bin;
  680. binjoin(Bins) ->
  681. binjoin(Bins, <<>>).
  682. binjoin([H | T], Acc) ->
  683. binjoin(T, <<H/binary, $,, Acc/binary>>);
  684. binjoin([], Acc) ->
  685. Acc.
  686. binfmt(Fmt, Args) -> iolist_to_binary(io_lib:format(Fmt, Args)).