emqx_gateway_api_authn.erl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2021-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_gateway_api_authn).
  17. -behaviour(minirest_api).
  18. -include("emqx_gateway_http.hrl").
  19. -include_lib("hocon/include/hoconsc.hrl").
  20. -include_lib("typerefl/include/types.hrl").
  21. -import(hoconsc, [mk/2, ref/2]).
  22. -import(emqx_dashboard_swagger, [error_codes/2]).
  23. -import(
  24. emqx_gateway_http,
  25. [
  26. return_http_error/2,
  27. with_gateway/2,
  28. with_authn/2
  29. ]
  30. ).
  31. %% minirest/dashbaord_swagger behaviour callbacks
  32. -export([
  33. api_spec/0,
  34. paths/0,
  35. schema/1
  36. ]).
  37. %% http handlers
  38. -export([
  39. authn/2,
  40. users/2,
  41. users_insta/2
  42. ]).
  43. %% internal export for emqx_gateway_api_listeners module
  44. -export([schema_authn/0]).
  45. -define(TAGS, [<<"Gateway Authentication">>]).
  46. %%--------------------------------------------------------------------
  47. %% minirest behaviour callbacks
  48. %%--------------------------------------------------------------------
  49. api_spec() ->
  50. emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}).
  51. paths() ->
  52. [
  53. "/gateways/:name/authentication",
  54. "/gateways/:name/authentication/users",
  55. "/gateways/:name/authentication/users/:uid"
  56. ].
  57. %%--------------------------------------------------------------------
  58. %% http handlers
  59. authn(get, #{bindings := #{name := Name0}}) ->
  60. with_gateway(Name0, fun(GwName, _) ->
  61. try emqx_gateway_http:authn(GwName) of
  62. Authn -> {200, Authn}
  63. catch
  64. error:{config_not_found, _} ->
  65. %% FIXME: should return 404?
  66. {204}
  67. end
  68. end);
  69. authn(put, #{
  70. bindings := #{name := Name0},
  71. body := Body
  72. }) ->
  73. with_gateway(Name0, fun(GwName, _) ->
  74. {ok, Authn} = emqx_gateway_http:update_authn(GwName, Body),
  75. {200, Authn}
  76. end);
  77. authn(post, #{
  78. bindings := #{name := Name0},
  79. body := Body
  80. }) ->
  81. with_gateway(Name0, fun(GwName, _) ->
  82. {ok, Authn} = emqx_gateway_http:add_authn(GwName, Body),
  83. {201, Authn}
  84. end);
  85. authn(delete, #{bindings := #{name := Name0}}) ->
  86. with_gateway(Name0, fun(GwName, _) ->
  87. ok = emqx_gateway_http:remove_authn(GwName),
  88. {204}
  89. end).
  90. users(get, #{bindings := #{name := Name0}, query_string := Qs}) ->
  91. with_authn(Name0, fun(
  92. _GwName,
  93. #{
  94. id := AuthId,
  95. chain_name := ChainName
  96. }
  97. ) ->
  98. emqx_authn_api:list_users(ChainName, AuthId, parse_qstring(Qs))
  99. end);
  100. users(post, #{
  101. bindings := #{name := Name0},
  102. body := Body
  103. }) ->
  104. with_authn(Name0, fun(
  105. _GwName,
  106. #{
  107. id := AuthId,
  108. chain_name := ChainName
  109. }
  110. ) ->
  111. emqx_authn_api:add_user(ChainName, AuthId, Body)
  112. end).
  113. users_insta(get, #{bindings := #{name := Name0, uid := UserId}}) ->
  114. with_authn(Name0, fun(
  115. _GwName,
  116. #{
  117. id := AuthId,
  118. chain_name := ChainName
  119. }
  120. ) ->
  121. emqx_authn_api:find_user(ChainName, AuthId, UserId)
  122. end);
  123. users_insta(put, #{
  124. bindings := #{name := Name0, uid := UserId},
  125. body := Body
  126. }) ->
  127. with_authn(Name0, fun(
  128. _GwName,
  129. #{
  130. id := AuthId,
  131. chain_name := ChainName
  132. }
  133. ) ->
  134. emqx_authn_api:update_user(ChainName, AuthId, UserId, Body)
  135. end);
  136. users_insta(delete, #{bindings := #{name := Name0, uid := UserId}}) ->
  137. with_authn(Name0, fun(
  138. _GwName,
  139. #{
  140. id := AuthId,
  141. chain_name := ChainName
  142. }
  143. ) ->
  144. emqx_authn_api:delete_user(ChainName, AuthId, UserId)
  145. end).
  146. %%--------------------------------------------------------------------
  147. %% Utils
  148. parse_qstring(Qs) ->
  149. maps:with(
  150. [
  151. <<"page">>,
  152. <<"limit">>,
  153. <<"like_user_id">>,
  154. <<"is_superuser">>
  155. ],
  156. Qs
  157. ).
  158. %%--------------------------------------------------------------------
  159. %% Swagger defines
  160. %%--------------------------------------------------------------------
  161. schema("/gateways/:name/authentication") ->
  162. #{
  163. 'operationId' => authn,
  164. get =>
  165. #{
  166. tags => ?TAGS,
  167. desc => ?DESC(get_authn),
  168. summary => <<"Get authenticator configuration">>,
  169. parameters => params_gateway_name_in_path(),
  170. responses =>
  171. ?STANDARD_RESP(
  172. #{
  173. 200 => schema_authn(),
  174. 204 => <<"Authenticator not initialized">>
  175. }
  176. )
  177. },
  178. put =>
  179. #{
  180. tags => ?TAGS,
  181. desc => ?DESC(update_authn),
  182. summary => <<"Update authenticator configuration">>,
  183. parameters => params_gateway_name_in_path(),
  184. 'requestBody' => schema_authn(),
  185. responses =>
  186. ?STANDARD_RESP(#{200 => schema_authn()})
  187. },
  188. post =>
  189. #{
  190. tags => ?TAGS,
  191. desc => ?DESC(add_authn),
  192. summary => <<"Create authenticator for gateway">>,
  193. parameters => params_gateway_name_in_path(),
  194. 'requestBody' => schema_authn(),
  195. responses =>
  196. ?STANDARD_RESP(#{201 => schema_authn()})
  197. },
  198. delete =>
  199. #{
  200. tags => ?TAGS,
  201. desc => ?DESC(delete_authn),
  202. summary => <<"Delete gateway authenticator">>,
  203. parameters => params_gateway_name_in_path(),
  204. responses =>
  205. ?STANDARD_RESP(#{204 => <<"Deleted">>})
  206. }
  207. };
  208. schema("/gateways/:name/authentication/users") ->
  209. #{
  210. 'operationId' => users,
  211. get =>
  212. #{
  213. tags => ?TAGS,
  214. desc => ?DESC(list_users),
  215. summary => <<"List users for gateway authenticator">>,
  216. parameters => params_gateway_name_in_path() ++
  217. params_paging_in_qs() ++
  218. params_fuzzy_in_qs(),
  219. responses =>
  220. ?STANDARD_RESP(
  221. #{
  222. 200 => emqx_dashboard_swagger:schema_with_example(
  223. ref(emqx_authn_api, response_users),
  224. emqx_authn_api:response_users_example()
  225. )
  226. }
  227. )
  228. },
  229. post =>
  230. #{
  231. tags => ?TAGS,
  232. desc => ?DESC(add_user),
  233. summary => <<"Add user for gateway authenticator">>,
  234. parameters => params_gateway_name_in_path(),
  235. 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
  236. ref(emqx_authn_api, request_user_create),
  237. emqx_authn_api:request_user_create_examples()
  238. ),
  239. responses =>
  240. ?STANDARD_RESP(
  241. #{
  242. 201 => emqx_dashboard_swagger:schema_with_example(
  243. ref(emqx_authn_api, response_user),
  244. emqx_authn_api:response_user_examples()
  245. )
  246. }
  247. )
  248. }
  249. };
  250. schema("/gateways/:name/authentication/users/:uid") ->
  251. #{
  252. 'operationId' => users_insta,
  253. get =>
  254. #{
  255. tags => ?TAGS,
  256. desc => ?DESC(get_user),
  257. summary => <<"Get user info for gateway authenticator">>,
  258. parameters => params_gateway_name_in_path() ++
  259. params_userid_in_path(),
  260. responses =>
  261. ?STANDARD_RESP(
  262. #{
  263. 200 => emqx_dashboard_swagger:schema_with_example(
  264. ref(emqx_authn_api, response_user),
  265. emqx_authn_api:response_user_examples()
  266. )
  267. }
  268. )
  269. },
  270. put =>
  271. #{
  272. tags => ?TAGS,
  273. desc => ?DESC(update_user),
  274. summary => <<"Update user info for gateway authenticator">>,
  275. parameters => params_gateway_name_in_path() ++
  276. params_userid_in_path(),
  277. 'requestBody' => emqx_dashboard_swagger:schema_with_examples(
  278. ref(emqx_authn_api, request_user_update),
  279. emqx_authn_api:request_user_update_examples()
  280. ),
  281. responses =>
  282. ?STANDARD_RESP(
  283. #{
  284. 200 => emqx_dashboard_swagger:schema_with_example(
  285. ref(emqx_authn_api, response_user),
  286. emqx_authn_api:response_user_examples()
  287. )
  288. }
  289. )
  290. },
  291. delete =>
  292. #{
  293. tags => ?TAGS,
  294. desc => ?DESC(delete_user),
  295. summary => <<"Delete user for gateway authenticator">>,
  296. parameters => params_gateway_name_in_path() ++
  297. params_userid_in_path(),
  298. responses =>
  299. ?STANDARD_RESP(#{204 => <<"User Deleted">>})
  300. }
  301. }.
  302. %%--------------------------------------------------------------------
  303. %% params defines
  304. params_gateway_name_in_path() ->
  305. [
  306. {name,
  307. mk(
  308. hoconsc:enum(emqx_gateway_schema:gateway_names()),
  309. #{
  310. in => path,
  311. desc => ?DESC(emqx_gateway_api, gateway_name_in_qs),
  312. example => <<"stomp">>
  313. }
  314. )}
  315. ].
  316. params_userid_in_path() ->
  317. [
  318. {uid,
  319. mk(
  320. binary(),
  321. #{
  322. in => path,
  323. desc => ?DESC(user_id),
  324. example => <<"test_username">>
  325. }
  326. )}
  327. ].
  328. params_paging_in_qs() ->
  329. emqx_dashboard_swagger:fields(page) ++
  330. emqx_dashboard_swagger:fields(limit).
  331. params_fuzzy_in_qs() ->
  332. [
  333. {like_user_id,
  334. mk(
  335. binary(),
  336. #{
  337. in => query,
  338. required => false,
  339. desc => ?DESC(like_user_id),
  340. example => <<"test_">>
  341. }
  342. )},
  343. {is_superuser,
  344. mk(
  345. boolean(),
  346. #{
  347. in => query,
  348. required => false,
  349. desc => ?DESC(is_superuser)
  350. }
  351. )}
  352. ].
  353. %%--------------------------------------------------------------------
  354. %% schemas
  355. schema_authn() ->
  356. emqx_dashboard_swagger:schema_with_examples(
  357. emqx_authn_schema:authenticator_type_without([emqx_authn_scram_mnesia_schema]),
  358. emqx_authn_api:authenticator_examples()
  359. ).