emqttd_auth_mysql.erl 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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. %%% emqttd authentication by mysql 'user' table.
  24. %%%
  25. %%% @end
  26. %%%-----------------------------------------------------------------------------
  27. -module(emqttd_auth_mysql).
  28. -author("Feng Lee <feng@emqtt.io>").
  29. -include("emqttd.hrl").
  30. -behaviour(emqttd_auth_mod).
  31. -export([init/1, check/3, description/0]).
  32. -define(NOT_LOADED, not_loaded(?LINE)).
  33. -record(state, {user_table, name_field, pass_field, pass_hash}).
  34. init(Opts) ->
  35. Mapper = proplists:get_value(field_mapper, Opts),
  36. {ok, #state{user_table = proplists:get_value(user_table, Opts, auth_user),
  37. name_field = proplists:get_value(username, Mapper),
  38. pass_field = proplists:get_value(password, Mapper),
  39. pass_hash = proplists:get_value(Opts, password_hash)}}.
  40. check(#mqtt_client{username = undefined}, _Password, _State) ->
  41. {error, "Username undefined"};
  42. check(_Client, undefined, _State) ->
  43. {error, "Password undefined"};
  44. check(#mqtt_client{username = Username}, Password,
  45. #state{user_table = UserTab, pass_hash = Type,
  46. name_field = NameField, pass_field = PassField}) ->
  47. Where = {'and', {NameField, Username}, {PassField, hash(Type, Password)}},
  48. if Type =:= pbkdf2 ->
  49. case emysql:select(UserTab, [PassField], {NameField, Username}) of
  50. {ok, []} -> {error, "User not exist"};
  51. {ok, Records} ->
  52. if length(Records) =:= 1 ->
  53. case pbkdf2_check(Password, lists:nth(Records, 1)) of
  54. true ->
  55. {ok, []};
  56. false ->
  57. {error, "UserName or Password is invalid"};
  58. ErrorInfo ->
  59. {error, ErrorInfo}
  60. end;
  61. true ->
  62. {error, "UserName is ambiguous"}
  63. end
  64. end;
  65. true ->
  66. case emysql:select(UserTab, Where) of
  67. {ok, []} -> {error, "Username or Password "};
  68. {ok, _Record} -> ok
  69. end
  70. end.
  71. description() -> "Authentication by MySQL".
  72. hash(plain, Password) ->
  73. Password;
  74. hash(md5, Password) ->
  75. hexstring(crypto:hash(md5, Password));
  76. hash(sha, Password) ->
  77. hexstring(crypto:hash(sha, Password)).
  78. hexstring(<<X:128/big-unsigned-integer>>) ->
  79. lists:flatten(io_lib:format("~32.16.0b", [X]));
  80. hexstring(<<X:160/big-unsigned-integer>>) ->
  81. lists:flatten(io_lib:format("~40.16.0b", [X])).
  82. not_loaded(Line) ->
  83. erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}).
  84. pbkdf2_check(Password, Pbkstr) ->
  85. case nif_pbkdf2_check(Password, Pbkstr) of
  86. {error, _} = Error ->
  87. throw(Error);
  88. IOData ->
  89. IOData
  90. end.
  91. nif_pbkdf2_check(Password, Pbkstr) ->
  92. ?NOT_LOADED.