emqx_auth_pgsql_SUITE.erl 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2020-2021 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. -compile(nowarn_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").
  26. -define(CREATE_ACL_TABLE, "CREATE TABLE mqtt_acl (
  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 (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").
  41. -define(CREATE_AUTH_TABLE, "CREATE TABLE mqtt_user (
  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 (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. emqx_ct:all(?MODULE).
  58. init_per_suite(Config) ->
  59. emqx_ct_helpers:start_apps([emqx_auth_pgsql]),
  60. drop_acl(),
  61. drop_auth(),
  62. init_auth(),
  63. init_acl(),
  64. set_special_configs(),
  65. Config.
  66. end_per_suite(Config) ->
  67. emqx_ct_helpers:stop_apps([emqx_auth_pgsql]),
  68. Config.
  69. set_special_configs() ->
  70. application:set_env(emqx, acl_nomatch, deny),
  71. application:set_env(emqx, allow_anonymous, false),
  72. application:set_env(emqx, enable_acl_cache, false).
  73. t_comment_config(_) ->
  74. AuthCount = length(emqx_hooks:lookup('client.authenticate')),
  75. AclCount = length(emqx_hooks:lookup('client.check_acl')),
  76. application:stop(?APP),
  77. [application:unset_env(?APP, Par) || Par <- [acl_query, auth_query]],
  78. application:start(?APP),
  79. ?assertEqual([], emqx_hooks:lookup('client.authenticate')),
  80. ?assertEqual(AuthCount - 1, length(emqx_hooks:lookup('client.authenticate'))),
  81. ?assertEqual(AclCount - 1, length(emqx_hooks:lookup('client.check_acl'))).
  82. t_placeholders(_) ->
  83. ClientA = #{username => <<"plain">>, clientid => <<"plain">>, zone => external},
  84. reload([{password_hash, plain},
  85. {auth_query, "select password from mqtt_user where username = '%u' and 'a_cn_val' = '%C' limit 1"}]),
  86. {error, not_authorized} =
  87. emqx_access_control:authenticate(ClientA#{password => <<"plain">>}),
  88. {error, not_authorized} =
  89. emqx_access_control:authenticate(ClientA#{password => <<"plain">>, cn => undefined}),
  90. {ok, _} =
  91. emqx_access_control:authenticate(ClientA#{password => <<"plain">>, cn => <<"a_cn_val">>}),
  92. reload([{auth_query, "select password from mqtt_user where username = '%c' and 'a_dn_val' = '%d' limit 1"}]),
  93. {error, not_authorized} =
  94. emqx_access_control:authenticate(ClientA#{password => <<"plain">>}),
  95. {error, not_authorized} =
  96. emqx_access_control:authenticate(ClientA#{password => <<"plain">>, dn => undefined}),
  97. {ok, _} =
  98. emqx_access_control:authenticate(ClientA#{password => <<"plain">>, dn => <<"a_dn_val">>}),
  99. reload([{auth_query, "select password from mqtt_user where username = '%u' and '192.168.1.5' = '%a' limit 1"}]),
  100. {error, not_authorized} =
  101. emqx_access_control:authenticate(ClientA#{password => <<"plain">>}),
  102. {ok, _} =
  103. emqx_access_control:authenticate(ClientA#{password => <<"plain">>, peerhost => {192,168,1,5}}).
  104. t_check_auth(_) ->
  105. Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external},
  106. Md5 = #{clientid => <<"md5">>, username => <<"md5">>, zone => external},
  107. Sha = #{clientid => <<"sha">>, username => <<"sha">>, zone => external},
  108. Sha256 = #{clientid => <<"sha256">>, username => <<"sha256">>, zone => external},
  109. Pbkdf2 = #{clientid => <<"pbkdf2_password">>, username => <<"pbkdf2_password">>, zone => external},
  110. BcryptFoo = #{clientid => <<"bcrypt_foo">>, username => <<"bcrypt_foo">>, zone => external},
  111. User1 = #{clientid => <<"bcrypt_foo">>, username => <<"user">>, zone => external},
  112. Bcrypt = #{clientid => <<"bcrypt">>, username => <<"bcrypt">>, zone => external},
  113. BcryptWrong = #{clientid => <<"bcrypt_wrong">>, username => <<"bcrypt_wrong">>, zone => external},
  114. reload([{password_hash, plain}]),
  115. {ok,#{is_superuser := true}} =
  116. emqx_access_control:authenticate(Plain#{password => <<"plain">>}),
  117. reload([{password_hash, md5}]),
  118. {ok,#{is_superuser := false}} =
  119. emqx_access_control:authenticate(Md5#{password => <<"md5">>}),
  120. reload([{password_hash, sha}]),
  121. {ok,#{is_superuser := false}} =
  122. emqx_access_control:authenticate(Sha#{password => <<"sha">>}),
  123. reload([{password_hash, sha256}]),
  124. {ok,#{is_superuser := false}} =
  125. emqx_access_control:authenticate(Sha256#{password => <<"sha256">>}),
  126. reload([{password_hash, bcrypt}]),
  127. {ok,#{is_superuser := false}} =
  128. emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}),
  129. {error, not_authorized} =
  130. emqx_access_control:authenticate(BcryptWrong#{password => <<"password">>}),
  131. %%pbkdf2 sha
  132. reload([{password_hash, {pbkdf2, sha, 1, 16}},
  133. {auth_query, "select password, salt from mqtt_user where username = '%u' limit 1"}]),
  134. {ok,#{is_superuser := false}} =
  135. emqx_access_control:authenticate(Pbkdf2#{password => <<"password">>}),
  136. reload([{password_hash, {salt, bcrypt}}]),
  137. {ok,#{is_superuser := false}} =
  138. emqx_access_control:authenticate(BcryptFoo#{password => <<"foo">>}),
  139. {error, _} = emqx_access_control:authenticate(User1#{password => <<"foo">>}),
  140. {error, not_authorized} = emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}).
  141. t_check_acl(_) ->
  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. deny = emqx_access_control:check_acl(User3, subscribe, <<"t2">>),%% nomatch -> ignore -> emqx acl
  151. deny = emqx_access_control:check_acl(User4, subscribe, <<"t1">>),%% nomatch -> ignore -> emqx 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).