emqx_auth_pgsql_SUITE.erl 10 KB

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