emqx_auth_pgsql_SUITE.erl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2020 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_auth_pgsql_SUITE).
  17. -compile(nowarn_export_all).
  18. -compile(export_all).
  19. -define(POOL, emqx_auth_pgsql).
  20. -define(APP, emqx_auth_pgsql).
  21. -include_lib("emqx/include/emqx.hrl").
  22. -include_lib("eunit/include/eunit.hrl").
  23. -include_lib("common_test/include/ct.hrl").
  24. %%setp1 init table
  25. -define(DROP_ACL_TABLE, "DROP TABLE IF EXISTS mqtt_acl_test").
  26. -define(CREATE_ACL_TABLE, "CREATE TABLE mqtt_acl_test (
  27. id SERIAL primary key,
  28. allow integer,
  29. ipaddr character varying(60),
  30. username character varying(100),
  31. clientid character varying(100),
  32. access integer,
  33. topic character varying(100))").
  34. -define(INIT_ACL, "INSERT INTO mqtt_acl_test (id, allow, ipaddr, username, clientid, access, topic)
  35. VALUES
  36. (1,1,'127.0.0.1','u1','c1',1,'t1'),
  37. (2,0,'127.0.0.1','u2','c2',1,'t1'),
  38. (3,1,'10.10.0.110','u1','c1',1,'t1'),
  39. (4,1,'127.0.0.1','u3','c3',3,'t1')").
  40. -define(DROP_AUTH_TABLE, "DROP TABLE IF EXISTS mqtt_user_test").
  41. -define(CREATE_AUTH_TABLE, "CREATE TABLE mqtt_user_test (
  42. id SERIAL primary key,
  43. is_superuser boolean,
  44. username character varying(100),
  45. password character varying(100),
  46. salt character varying(40))").
  47. -define(INIT_AUTH, "INSERT INTO mqtt_user_test (id, is_superuser, username, password, salt)
  48. VALUES
  49. (1, true, 'plain', 'plain', 'salt'),
  50. (2, false, 'md5', '1bc29b36f623ba82aaf6724fd3b16718', 'salt'),
  51. (3, false, 'sha', 'd8f4590320e1343a915b6394170650a8f35d6926', 'salt'),
  52. (4, false, 'sha256', '5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e', 'salt'),
  53. (5, false, 'pbkdf2_password', 'cdedb5281bb2f801565a1122b2563515', 'ATHENA.MIT.EDUraeburn'),
  54. (6, false, 'bcrypt_foo', '$2a$12$sSS8Eg.ovVzaHzi1nUHYK.HbUIOdlQI0iS22Q5rd5z.JVVYH6sfm6', '$2a$12$sSS8Eg.ovVzaHzi1nUHYK.'),
  55. (7, false, 'bcrypt', '$2y$16$rEVsDarhgHYB0TGnDFJzyu5f.T.Ha9iXMTk9J36NCMWWM7O16qyaK', 'salt')").
  56. all() ->
  57. [{group, ssl}, {group, nossl}].
  58. groups() ->
  59. Cases = emqx_ct:all(?MODULE),
  60. [{ssl, [sequence], Cases}, {nossl, [sequence], Cases}].
  61. init_per_group(Name, Config) ->
  62. case Name of
  63. ssl ->
  64. emqx_ct_helpers:start_apps([emqx_auth_pgsql], fun set_special_configs_ssl/1);
  65. nossl ->
  66. emqx_ct_helpers:start_apps([emqx_auth_pgsql], fun set_special_configs/1)
  67. end,
  68. init_auth_(),
  69. init_acl_(),
  70. Config.
  71. end_per_group(_, Config) ->
  72. drop_auth_(),
  73. drop_acl_(),
  74. emqx_ct_helpers:stop_apps([emqx_auth_pgsql]),
  75. Config.
  76. set_special_configs_ssl(Name) ->
  77. Server = application:get_env(?APP, server, []),
  78. Path = emqx_ct_helpers:deps_path(emqx_auth_pgsql, "test/emqx_auth_pgsql_SUITE_data/"),
  79. Sslopts = [{keyfile, Path ++ "/client-key.pem"},
  80. {certfile, Path ++ "/client-cert.pem"},
  81. {cacertfile, Path ++ "/ca.pem"}],
  82. Temp = lists:keyreplace(ssl, 1, Server, {ssl, true}),
  83. application:set_env(?APP, server, Temp),
  84. application:set_env(?APP, server, lists:keyreplace(ssl_opts, 1, Temp, {ssl_opts, Sslopts})),
  85. set_special_configs(Name).
  86. set_special_configs(emqx) ->
  87. application:set_env(emqx, acl_nomatch, deny),
  88. application:set_env(emqx, acl_file,
  89. emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/acl.conf")),
  90. application:set_env(emqx, allow_anonymous, false),
  91. application:set_env(emqx, enable_acl_cache, false),
  92. application:set_env(emqx, plugins_loaded_file,
  93. emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/loaded_plugins"));
  94. set_special_configs(emqx_auth_pgsql) ->
  95. Server = application:get_env(?APP, server, []),
  96. application:set_env(?APP, server,
  97. lists:keyreplace(password,
  98. 1,
  99. lists:keyreplace(pool_size, 1, Server, {pool_size, 1}),
  100. {password, "public"})),
  101. application:set_env(?APP, acl_query, "select allow, ipaddr, username, clientid, access, topic from mqtt_acl_test where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'"),
  102. application:set_env(?APP, super_query, "select is_superuser from mqtt_user_test where username = '%u' limit 1"),
  103. application:set_env(?APP, auth_query, "select password from mqtt_user_test where username = '%u' limit 1");
  104. set_special_configs(_App) ->
  105. ok.
  106. t_comment_config(_) ->
  107. AuthCount = length(emqx_hooks:lookup('client.authenticate')),
  108. AclCount = length(emqx_hooks:lookup('client.check_acl')),
  109. application:stop(?APP),
  110. [application:unset_env(?APP, Par) || Par <- [acl_query, auth_query]],
  111. application:start(?APP),
  112. ?assertEqual([], emqx_hooks:lookup('client.authenticate')),
  113. ?assertEqual(AuthCount - 1, length(emqx_hooks:lookup('client.authenticate'))),
  114. ?assertEqual(AclCount - 1, length(emqx_hooks:lookup('client.check_acl'))).
  115. t_placeholders(_) ->
  116. ClientA = #{username => <<"plain">>, clientid => <<"plain">>, zone => external},
  117. reload([{password_hash, plain},
  118. {auth_query, "select password from mqtt_user_test where username = '%u' and 'a_cn_val' = '%C' limit 1"}]),
  119. {error, not_authorized} =
  120. emqx_access_control:authenticate(ClientA#{password => <<"plain">>}),
  121. {error, not_authorized} =
  122. emqx_access_control:authenticate(ClientA#{password => <<"plain">>, cn => undefined}),
  123. {ok, _} =
  124. emqx_access_control:authenticate(ClientA#{password => <<"plain">>, cn => <<"a_cn_val">>}),
  125. reload([{auth_query, "select password from mqtt_user_test where username = '%c' and 'a_dn_val' = '%d' limit 1"}]),
  126. {error, not_authorized} =
  127. emqx_access_control:authenticate(ClientA#{password => <<"plain">>}),
  128. {error, not_authorized} =
  129. emqx_access_control:authenticate(ClientA#{password => <<"plain">>, dn => undefined}),
  130. {ok, _} =
  131. emqx_access_control:authenticate(ClientA#{password => <<"plain">>, dn => <<"a_dn_val">>}),
  132. reload([{auth_query, "select password from mqtt_user_test where username = '%u' and '192.168.1.5' = '%a' limit 1"}]),
  133. {error, not_authorized} =
  134. emqx_access_control:authenticate(ClientA#{password => <<"plain">>}),
  135. {ok, _} =
  136. emqx_access_control:authenticate(ClientA#{password => <<"plain">>, peerhost => {192,168,1,5}}).
  137. t_check_auth(_) ->
  138. Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external},
  139. Md5 = #{clientid => <<"md5">>, username => <<"md5">>, zone => external},
  140. Sha = #{clientid => <<"sha">>, username => <<"sha">>, zone => external},
  141. Sha256 = #{clientid => <<"sha256">>, username => <<"sha256">>, zone => external},
  142. Pbkdf2 = #{clientid => <<"pbkdf2_password">>, username => <<"pbkdf2_password">>, zone => external},
  143. BcryptFoo = #{clientid => <<"bcrypt_foo">>, username => <<"bcrypt_foo">>, zone => external},
  144. User1 = #{clientid => <<"bcrypt_foo">>, username => <<"user">>, zone => external},
  145. Bcrypt = #{clientid => <<"bcrypt">>, username => <<"bcrypt">>, zone => external},
  146. reload([{password_hash, plain},
  147. {auth_query, "select password from mqtt_user_test where username = '%u' limit 1"}]),
  148. {ok, #{is_superuser := true}} = emqx_access_control:authenticate(Plain#{password => <<"plain">>}),
  149. reload([{password_hash, md5}]),
  150. {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Md5#{password => <<"md5">>}),
  151. reload([{password_hash, sha}]),
  152. {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Sha#{password => <<"sha">>}),
  153. reload([{password_hash, sha256}]),
  154. {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Sha256#{password => <<"sha256">>}),
  155. reload([{password_hash, bcrypt}]),
  156. {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}),
  157. %%pbkdf2 sha
  158. reload([{password_hash, {pbkdf2, sha, 1, 16}}, {auth_query, "select password, salt from mqtt_user_test where username = '%u' limit 1"}]),
  159. {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Pbkdf2#{password => <<"password">>}),
  160. reload([{password_hash, {salt, bcrypt}}]),
  161. {ok, #{is_superuser := false}} = emqx_access_control:authenticate(BcryptFoo#{password => <<"foo">>}),
  162. {error, _} = emqx_access_control:authenticate(User1#{password => <<"foo">>}),
  163. {error, not_authorized} = emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}).
  164. t_check_acl(_) ->
  165. emqx_modules:load_module(emqx_mod_acl_internal, false),
  166. User1 = #{zone => external, peerhost => {127,0,0,1}, clientid => <<"c1">>, username => <<"u1">>},
  167. User2 = #{zone => external, peerhost => {127,0,0,1}, clientid => <<"c2">>, username => <<"u2">>},
  168. reload([{acl_query, "select allow, ipaddr, username, clientid, access, topic from mqtt_acl_test where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'"}]),
  169. allow = emqx_access_control:check_acl(User1, subscribe, <<"t1">>),
  170. deny = emqx_access_control:check_acl(User2, subscribe, <<"t1">>),
  171. User3 = #{zone => external, peerhost => {10,10,0,110}, clientid => <<"c1">>, username => <<"u1">>},
  172. User4 = #{zone => external, peerhost => {10,10,10,110}, clientid => <<"c1">>, username => <<"u1">>},
  173. allow = emqx_access_control:check_acl(User3, subscribe, <<"t1">>),
  174. allow = emqx_access_control:check_acl(User3, subscribe, <<"t1">>),
  175. allow = emqx_access_control:check_acl(User3, subscribe, <<"t2">>),%% nomatch -> ignore -> emqttd acl
  176. allow = emqx_access_control:check_acl(User4, subscribe, <<"t1">>),%% nomatch -> ignore -> emqttd acl
  177. User5 = #{zone => external, peerhost => {127,0,0,1}, clientid => <<"c3">>, username => <<"u3">>},
  178. allow = emqx_access_control:check_acl(User5, subscribe, <<"t1">>),
  179. allow = emqx_access_control:check_acl(User5, publish, <<"t1">>).
  180. t_acl_super(_) ->
  181. reload([{password_hash, plain}, {auth_query, "select password from mqtt_user_test where username = '%u' limit 1"}]),
  182. {ok, C} = emqtt:start_link([{host, "localhost"}, {clientid, <<"simpleClient">>},
  183. {username, <<"plain">>}, {password, <<"plain">>}]),
  184. {ok, _} = emqtt:connect(C),
  185. timer:sleep(10),
  186. emqtt:subscribe(C, <<"TopicA">>, qos2),
  187. emqtt:publish(C, <<"TopicA">>, <<"Payload">>, qos2),
  188. timer:sleep(1000),
  189. receive
  190. {publish, #{payload := Payload}} ->
  191. ?assertEqual(<<"Payload">>, Payload)
  192. after
  193. 1000 ->
  194. ct:fail({receive_timeout, <<"Payload">>}),
  195. ok
  196. end,
  197. emqtt:disconnect(C).
  198. reload(Config) when is_list(Config) ->
  199. application:stop(?APP),
  200. [application:set_env(?APP, K, V) || {K, V} <- Config],
  201. application:start(?APP).
  202. init_acl_() ->
  203. {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?POOL})),
  204. {ok, [], []} = epgsql:squery(Pid, ?DROP_ACL_TABLE),
  205. {ok, [], []} = epgsql:squery(Pid, ?CREATE_ACL_TABLE),
  206. {ok, _} = epgsql:equery(Pid, ?INIT_ACL).
  207. drop_acl_() ->
  208. {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?POOL})),
  209. {ok, [], []}= epgsql:squery(Pid, ?DROP_ACL_TABLE).
  210. init_auth_() ->
  211. {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?POOL})),
  212. {ok, [], []} = epgsql:squery(Pid, ?DROP_AUTH_TABLE),
  213. {ok, [], []} = epgsql:squery(Pid, ?CREATE_AUTH_TABLE),
  214. {ok, _} = epgsql:equery(Pid, ?INIT_AUTH).
  215. drop_auth_() ->
  216. {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?POOL})),
  217. {ok, [], []} = epgsql:squery(Pid, ?DROP_AUTH_TABLE).