emqttd_auth_username.erl 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. %%%-----------------------------------------------------------------------------
  2. %%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
  3. %%%
  4. %%% Permission is hereby granted, free of charge, to any person obtaining a copy
  5. %%% of this software and associated documentation files (the "Software"), to deal
  6. %%% in the Software without restriction, including without limitation the rights
  7. %%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. %%% copies of the Software, and to permit persons to whom the Software is
  9. %%% furnished to do so, subject to the following conditions:
  10. %%%
  11. %%% The above copyright notice and this permission notice shall be included in all
  12. %%% copies or substantial portions of the Software.
  13. %%%
  14. %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. %%% SOFTWARE.
  21. %%%-----------------------------------------------------------------------------
  22. %%% @doc
  23. %%% Authentication with username and password.
  24. %%%
  25. %%% @end
  26. %%%-----------------------------------------------------------------------------
  27. -module(emqttd_auth_username).
  28. -author("Feng Lee <feng@emqtt.io>").
  29. -include("emqttd.hrl").
  30. -include("emqttd_cli.hrl").
  31. %% CLI callbacks
  32. -export([cli/1]).
  33. -behaviour(emqttd_auth_mod).
  34. -export([add_user/2, remove_user/1,
  35. lookup_user/1, all_users/0]).
  36. %% emqttd_auth callbacks
  37. -export([init/1, check/3, description/0]).
  38. -define(AUTH_USERNAME_TAB, mqtt_auth_username).
  39. -record(?AUTH_USERNAME_TAB, {username, password}).
  40. %%%=============================================================================
  41. %%% CLI
  42. %%%=============================================================================
  43. cli(["add", Username, Password]) ->
  44. ?PRINT("~p~n", [add_user(list_to_binary(Username), list_to_binary(Password))]);
  45. cli(["del", Username]) ->
  46. ?PRINT("~p~n", [remove_user(list_to_binary(Username))]);
  47. cli(_) ->
  48. ?USAGE([{"users add <Username> <Password>", "add user"},
  49. {"users del <Username>", "delete user"}]).
  50. %%%=============================================================================
  51. %%% API
  52. %%%=============================================================================
  53. %%------------------------------------------------------------------------------
  54. %% @doc Add user
  55. %% @end
  56. %%------------------------------------------------------------------------------
  57. -spec add_user(binary(), binary()) -> {atomic, ok} | {aborted, any()}.
  58. add_user(Username, Password) ->
  59. User = #?AUTH_USERNAME_TAB{username = Username, password = hash(Password)},
  60. mnesia:transaction(fun mnesia:write/1, [User]).
  61. %%------------------------------------------------------------------------------
  62. %% @doc Lookup user by username
  63. %% @end
  64. %%------------------------------------------------------------------------------
  65. -spec lookup_user(binary()) -> list().
  66. lookup_user(Username) ->
  67. mnesia:dirty_read(?AUTH_USERNAME_TAB, Username).
  68. %%------------------------------------------------------------------------------
  69. %% @doc Remove user
  70. %% @end
  71. %%------------------------------------------------------------------------------
  72. -spec remove_user(binary()) -> {atomic, ok} | {aborted, any()}.
  73. remove_user(Username) ->
  74. mnesia:transaction(fun mnesia:delete/1, [{?AUTH_USERNAME_TAB, Username}]).
  75. %%------------------------------------------------------------------------------
  76. %% @doc All usernames
  77. %% @end
  78. %%------------------------------------------------------------------------------
  79. -spec all_users() -> list().
  80. all_users() ->
  81. mnesia:dirty_all_keys(?AUTH_USERNAME_TAB).
  82. %%%=============================================================================
  83. %%% emqttd_auth callbacks
  84. %%%=============================================================================
  85. init(Opts) ->
  86. mnesia:create_table(?AUTH_USERNAME_TAB, [
  87. {disc_copies, [node()]},
  88. {attributes, record_info(fields, ?AUTH_USERNAME_TAB)}]),
  89. mnesia:add_table_copy(?AUTH_USERNAME_TAB, node(), disc_copies),
  90. emqttd_ctl:register_cmd(users, {?MODULE, cli}, []),
  91. {ok, Opts}.
  92. check(#mqtt_client{username = undefined}, _Password, _Opts) ->
  93. {error, "Username undefined"};
  94. check(_User, undefined, _Opts) ->
  95. {error, "Password undefined"};
  96. check(#mqtt_client{username = Username}, Password, _Opts) ->
  97. case mnesia:dirty_read(?AUTH_USERNAME_TAB, Username) of
  98. [] ->
  99. {error, "Username Not Found"};
  100. [#?AUTH_USERNAME_TAB{password = <<Salt:4/binary, Hash/binary>>}] ->
  101. case Hash =:= md5_hash(Salt, Password) of
  102. true -> ok;
  103. false -> {error, "Password Not Right"}
  104. end
  105. end.
  106. description() ->
  107. "Username password authentication module".
  108. %%%=============================================================================
  109. %%% Internal functions
  110. %%%=============================================================================
  111. hash(Password) ->
  112. SaltBin = salt(),
  113. <<SaltBin/binary, (md5_hash(SaltBin, Password))/binary>>.
  114. md5_hash(SaltBin, Password) ->
  115. erlang:md5(<<SaltBin/binary, Password/binary>>).
  116. salt() ->
  117. {A1,A2,A3} = now(),
  118. random:seed(A1, A2, A3),
  119. Salt = random:uniform(16#ffffffff),
  120. <<Salt:32>>.