emqx_authz_api_schema.erl 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2020-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. %%
  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_schema).
  17. -include("emqx_authz.hrl").
  18. -include_lib("hocon/include/hoconsc.hrl").
  19. -include_lib("emqx_connector/include/emqx_connector.hrl").
  20. -import(hoconsc, [mk/2, enum/1]).
  21. -import(emqx_schema, [mk_duration/2]).
  22. -export([
  23. fields/1,
  24. authz_sources_types/1
  25. ]).
  26. %%------------------------------------------------------------------------------
  27. %% Hocon Schema
  28. %%------------------------------------------------------------------------------
  29. fields(file) ->
  30. authz_common_fields(file) ++
  31. [
  32. {rules, #{
  33. type => binary(),
  34. required => true,
  35. example =>
  36. <<
  37. "{allow,{username,{re,\"^dashboard$\"}},subscribe,[\"$SYS/#\"]}.\n",
  38. "{allow,{ipaddr,\"127.0.0.1\"},all,[\"$SYS/#\",\"#\"]}."
  39. >>,
  40. desc => ?DESC(rules)
  41. }}
  42. ];
  43. fields(http_get) ->
  44. [
  45. {method, #{type => get, default => get, required => true, desc => ?DESC(method)}},
  46. {headers, fun headers_no_content_type/1}
  47. ] ++ authz_http_common_fields();
  48. fields(http_post) ->
  49. [
  50. {method, #{type => post, default => post, required => true, desc => ?DESC(method)}},
  51. {headers, fun headers/1}
  52. ] ++ authz_http_common_fields();
  53. fields(built_in_database) ->
  54. authz_common_fields(built_in_database);
  55. fields(mongo_single) ->
  56. authz_mongo_common_fields() ++
  57. emqx_mongodb:fields(single);
  58. fields(mongo_rs) ->
  59. authz_mongo_common_fields() ++
  60. emqx_mongodb:fields(rs);
  61. fields(mongo_sharded) ->
  62. authz_mongo_common_fields() ++
  63. emqx_mongodb:fields(sharded);
  64. fields(mysql) ->
  65. authz_common_fields(mysql) ++
  66. [{query, query()}] ++
  67. proplists:delete(prepare_statement, emqx_mysql:fields(config));
  68. fields(postgresql) ->
  69. authz_common_fields(postgresql) ++
  70. [{query, query()}] ++
  71. proplists:delete(prepare_statement, emqx_connector_pgsql:fields(config));
  72. fields(redis_single) ->
  73. authz_redis_common_fields() ++
  74. emqx_redis:fields(single);
  75. fields(redis_sentinel) ->
  76. authz_redis_common_fields() ++
  77. emqx_redis:fields(sentinel);
  78. fields(redis_cluster) ->
  79. authz_redis_common_fields() ++
  80. emqx_redis:fields(cluster);
  81. fields(position) ->
  82. [
  83. {position,
  84. mk(
  85. string(),
  86. #{
  87. desc => ?DESC(position),
  88. required => true,
  89. in => body
  90. }
  91. )}
  92. ].
  93. %%------------------------------------------------------------------------------
  94. %% http type funcs
  95. authz_http_common_fields() ->
  96. authz_common_fields(http) ++
  97. [
  98. {url, fun url/1},
  99. {body,
  100. hoconsc:mk(map([{fuzzy, term(), binary()}]), #{
  101. required => false, desc => ?DESC(body)
  102. })},
  103. {request_timeout,
  104. mk_duration("Request timeout", #{
  105. required => false, default => <<"30s">>, desc => ?DESC(request_timeout)
  106. })}
  107. ] ++
  108. maps:to_list(
  109. maps:without(
  110. [
  111. pool_type
  112. ],
  113. maps:from_list(emqx_connector_http:fields(config))
  114. )
  115. ).
  116. url(type) -> binary();
  117. url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
  118. url(required) -> true;
  119. url(desc) -> ?DESC(?FUNCTION_NAME);
  120. url(_) -> undefined.
  121. headers(type) ->
  122. map();
  123. headers(desc) ->
  124. ?DESC(?FUNCTION_NAME);
  125. headers(converter) ->
  126. fun(Headers) ->
  127. maps:merge(default_headers(), transform_header_name(Headers))
  128. end;
  129. headers(default) ->
  130. default_headers();
  131. headers(_) ->
  132. undefined.
  133. headers_no_content_type(type) ->
  134. map();
  135. headers_no_content_type(desc) ->
  136. ?DESC(?FUNCTION_NAME);
  137. headers_no_content_type(converter) ->
  138. fun(Headers) ->
  139. maps:without(
  140. [<<"content-type">>],
  141. maps:merge(default_headers_no_content_type(), transform_header_name(Headers))
  142. )
  143. end;
  144. headers_no_content_type(default) ->
  145. default_headers_no_content_type();
  146. headers_no_content_type(_) ->
  147. undefined.
  148. %% headers
  149. default_headers() ->
  150. maps:put(
  151. <<"content-type">>,
  152. <<"application/json">>,
  153. default_headers_no_content_type()
  154. ).
  155. default_headers_no_content_type() ->
  156. #{
  157. <<"accept">> => <<"application/json">>,
  158. <<"cache-control">> => <<"no-cache">>,
  159. <<"connection">> => <<"keep-alive">>,
  160. <<"keep-alive">> => <<"timeout=30, max=1000">>
  161. }.
  162. transform_header_name(Headers) ->
  163. maps:fold(
  164. fun(K0, V, Acc) ->
  165. K = list_to_binary(string:to_lower(to_list(K0))),
  166. maps:put(K, V, Acc)
  167. end,
  168. #{},
  169. Headers
  170. ).
  171. %%------------------------------------------------------------------------------
  172. %% MonogDB type funcs
  173. authz_mongo_common_fields() ->
  174. authz_common_fields(mongodb) ++
  175. [
  176. {collection, fun collection/1},
  177. {filter, fun filter/1}
  178. ].
  179. collection(type) -> binary();
  180. collection(desc) -> ?DESC(?FUNCTION_NAME);
  181. collection(required) -> true;
  182. collection(_) -> undefined.
  183. filter(type) ->
  184. map();
  185. filter(desc) ->
  186. ?DESC(?FUNCTION_NAME);
  187. filter(required) ->
  188. false;
  189. filter(default) ->
  190. #{};
  191. filter(_) ->
  192. undefined.
  193. %%------------------------------------------------------------------------------
  194. %% Redis type funcs
  195. authz_redis_common_fields() ->
  196. authz_common_fields(redis) ++
  197. [
  198. {cmd,
  199. mk(binary(), #{
  200. required => true,
  201. desc => ?DESC(cmd),
  202. example => <<"HGETALL mqtt_authz">>
  203. })}
  204. ].
  205. %%------------------------------------------------------------------------------
  206. %% Authz api type funcs
  207. authz_common_fields(Type) when is_atom(Type) ->
  208. [
  209. {enable, fun enable/1},
  210. {type, #{
  211. type => Type,
  212. default => Type,
  213. required => true,
  214. desc => ?DESC(type),
  215. in => body
  216. }}
  217. ].
  218. enable(type) -> boolean();
  219. enable(default) -> true;
  220. enable(desc) -> ?DESC(?FUNCTION_NAME);
  221. enable(_) -> undefined.
  222. %%------------------------------------------------------------------------------
  223. %% Authz DB query
  224. query() ->
  225. #{
  226. type => binary(),
  227. desc => ?DESC(query),
  228. required => true,
  229. validator => fun(S) ->
  230. case size(S) > 0 of
  231. true -> ok;
  232. _ -> {error, "Request query"}
  233. end
  234. end
  235. }.
  236. %%------------------------------------------------------------------------------
  237. %% Internal funcs
  238. authz_sources_types(Type) ->
  239. case Type of
  240. simple ->
  241. [http, mongodb, redis];
  242. detailed ->
  243. [
  244. http_get,
  245. http_post,
  246. mongo_single,
  247. mongo_rs,
  248. mongo_sharded,
  249. redis_single,
  250. redis_sentinel,
  251. redis_cluster
  252. ]
  253. end ++
  254. [
  255. built_in_database,
  256. mysql,
  257. postgresql,
  258. file
  259. ].
  260. to_list(A) when is_atom(A) ->
  261. atom_to_list(A);
  262. to_list(B) when is_binary(B) ->
  263. binary_to_list(B).