Advertisement
Guest User

Untitled

a guest
Aug 29th, 2017
458
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.00 KB | None | 0 0
  1. %%%----------------------------------------------------------------------
  2. %%% File : ejabberd_auth_http.erl
  3. %%% Author : Piotr Nosek <piotr.nosek@erlang-solutions.com>
  4. %%% Purpose : Authentication via HTTP request
  5. %%% Created : 23 Sep 2013 by Piotr Nosek <piotr.nosek@erlang-solutions.com>
  6. %%%----------------------------------------------------------------------
  7.  
  8. -module(ejabberd_auth_http).
  9. -author('piotr.nosek@erlang-solutions.com').
  10.  
  11. -behaviour(ejabberd_auth).
  12.  
  13. -behaviour(ejabberd_config).
  14.  
  15. %% External exports
  16. -export([start/1,
  17. set_password/3,
  18. check_password/4,
  19. check_password/6,
  20. try_register/3,
  21. get_password/2,
  22. get_password_s/2,
  23. user_exists/2,
  24. remove_user/2,
  25. remove_user/3,
  26. plain_password_required/1,
  27. store_type/1,
  28. login/2,
  29. get_password/3,
  30. opt_type/1,
  31. stop/1]).
  32.  
  33. -export([is_user_exists/2]).
  34.  
  35. -include("ejabberd.hrl").
  36. -include("logger.hrl").
  37.  
  38. %%%----------------------------------------------------------------------
  39. %%% API
  40. %%%----------------------------------------------------------------------
  41.  
  42. opt_type(auth_opts) -> fun (V) -> V end;
  43. opt_type(_) -> [auth_opts].
  44.  
  45. -spec start(binary()) -> ok.
  46. start(Host) ->
  47. AuthOpts = ejabberd_config:get_option({auth_opts, Host}, fun(V) -> V end),
  48. {_, AuthHost} = lists:keyfind(host, 1, AuthOpts),
  49. PoolSize = proplists:get_value(connection_pool_size, AuthOpts, 10),
  50. Opts = proplists:get_value(connection_opts, AuthOpts, []),
  51. ChildMods = [fusco],
  52. ChildMFA = {fusco, start_link, [binary_to_list(AuthHost), Opts]},
  53. Proc = gen_mod:get_module_proc(Host, ?MODULE),
  54. ChildSpec = {Proc, {cuesport, start_link,
  55. [pool_name(Host), PoolSize, ChildMods, ChildMFA]},
  56. transient, 2000, supervisor, [cuesport | ChildMods]},
  57. supervisor:start_child(ejabberd_backend_sup, ChildSpec).
  58.  
  59. -spec plain_password_required(binary()) -> false.
  60. plain_password_required(Server) ->
  61. store_type(Server) == scram.
  62.  
  63. -spec store_type(binary()) -> plain | scram.
  64. store_type(Server) ->
  65. ejabberd_auth:password_format(Server).
  66.  
  67. -spec check_password(ejabberd:luser(), binary(), ejabberd:lserver(), binary()) -> boolean().
  68. check_password(LUser, _AuthzId, LServer, Password) ->
  69. case scram2:enabled(LServer) of
  70. false ->
  71. case make_req(get, <<"check_password">>, LUser, LServer, Password) of
  72. {ok, <<"true">>} -> true;
  73. _ -> false
  74. end;
  75. true ->
  76. {ok, true} =:= verify_scram_password(LUser, LServer, Password)
  77. end.
  78.  
  79. -spec check_password(ejabberd:luser(), binary(), ejabberd:lserver(), binary(), binary(), fun()) -> boolean().
  80. check_password(LUser, _AuthzId, LServer, Password, Digest, DigestGen) ->
  81. case make_req(get, <<"get_password">>, LUser, LServer, <<"">>) of
  82. {error, _} ->
  83. false;
  84. {ok, GotPasswd} ->
  85. case scram2:enabled(LServer) of
  86. true ->
  87. case scram2:deserialize(GotPasswd) of
  88. {ok, #scram{} = Scram} ->
  89. scram2:check_digest(Scram, Digest, DigestGen, Password);
  90. _ ->
  91. false
  92. end;
  93. false ->
  94. check_digest(Digest, DigestGen, Password, GotPasswd)
  95. end
  96. end.
  97.  
  98. -spec check_digest(binary(), fun(), binary(), binary()) -> boolean().
  99. check_digest(Digest, DigestGen, Password, Passwd) ->
  100. DigRes = if
  101. Digest /= <<>> ->
  102. Digest == DigestGen(Passwd);
  103. true ->
  104. false
  105. end,
  106. if DigRes ->
  107. true;
  108. true ->
  109. (Passwd == Password) and (Password /= <<>>)
  110. end.
  111.  
  112.  
  113. -spec set_password(ejabberd:luser(), ejabberd:lserver(), binary()) -> ok | {error, term()}.
  114. set_password(LUser, LServer, Password) ->
  115. PasswordFinal = case scram2:enabled(LServer) of
  116. true -> scram2:serialize(scram2:password_to_scram(
  117. Password, scram2:iterations(LServer)));
  118. false -> Password
  119. end,
  120. case make_req(post, <<"set_password">>, LUser, LServer, PasswordFinal) of
  121. {error, _} = Err -> Err;
  122. _ -> ok
  123. end.
  124.  
  125. -spec try_register(ejabberd:luser(), ejabberd:lserver(), binary()) -> ok | {error, atom()}.
  126. try_register(LUser, LServer, Password) ->
  127. PasswordFinal = case scram2:enabled(LServer) of
  128. true -> scram2:serialize(scram2:password_to_scram(
  129. Password, scram2:iterations(LServer)));
  130. false -> Password
  131. end,
  132. case make_req(post, <<"register">>, LUser, LServer, PasswordFinal) of
  133. {ok, created} -> ok;
  134. {error, conflict} -> {error, exists};
  135. Error -> Error
  136. end.
  137.  
  138. -spec get_password(ejabberd:luser(), ejabberd:lserver()) -> false | binary() |
  139. {binary(), binary(), binary(), integer()}.
  140. get_password(LUser, LServer) ->
  141. case make_req(get, <<"get_password">>, LUser, LServer, <<"">>) of
  142. {error, _} ->
  143. false;
  144. {ok, Password} ->
  145. case scram2:enabled(LServer) of
  146. true ->
  147. case scram2:deserialize(Password) of
  148. {ok, #scram{} = Scram} ->
  149. scram2:scram_to_tuple(Scram);
  150. _ ->
  151. false
  152. end;
  153. false ->
  154. Password
  155. end
  156. end.
  157.  
  158. -spec get_password_s(ejabberd:luser(), ejabberd:lserver()) -> binary().
  159. get_password_s(User, Server) ->
  160. case get_password(User, Server) of
  161. Pass when is_binary(Pass) -> Pass;
  162. _ -> <<>>
  163. end.
  164.  
  165. -spec user_exists(ejabberd:luser(), ejabberd:lserver()) -> boolean().
  166. user_exists(LUser, LServer) ->
  167. case make_req(get, <<"user_exists">>, LUser, LServer, <<"">>) of
  168. {ok, <<"true">>} -> true;
  169. _ -> false
  170. end.
  171.  
  172. -spec remove_user(ejabberd:luser(), ejabberd:lserver()) -> ok | not_exists | not_allowed | bad_request.
  173. remove_user(LUser, LServer) ->
  174. remove_user_req(LUser, LServer, <<"">>, <<"remove_user">>).
  175.  
  176. -spec remove_user(ejabberd:luser(), ejabberd:lserver(), binary()) -> ok | not_exists | not_allowed | bad_request.
  177. remove_user(LUser, LServer, Password) ->
  178. case scram2:enabled(LServer) of
  179. false ->
  180. remove_user_req(LUser, LServer, Password, <<"remove_user_validate">>);
  181. true ->
  182. case verify_scram_password(LUser, LServer, Password) of
  183. {ok, false} ->
  184. not_allowed;
  185. {ok, true} ->
  186. remove_user_req(LUser, LServer, <<"">>, <<"remove_user">>);
  187. {error, Error} ->
  188. Error
  189. end
  190. end.
  191.  
  192. -spec remove_user_req(binary(), binary(), binary(), binary()) ->
  193. ok | not_exists | not_allowed | bad_request.
  194. remove_user_req(LUser, LServer, Password, Method) ->
  195. case make_req(post, Method, LUser, LServer, Password) of
  196. {error, not_allowed} -> not_allowed;
  197. {error, not_found} -> not_exists;
  198. {error, _} -> bad_request;
  199. _ -> ok
  200. end.
  201.  
  202. %%%----------------------------------------------------------------------
  203. %%% Request maker
  204. %%%----------------------------------------------------------------------
  205.  
  206. -spec make_req(post | get, binary(), binary(), binary(), binary()) ->
  207. {ok, Body :: binary()} | {error, term()}.
  208. make_req(_, _, LUser, LServer, _) when LUser == error orelse LServer == error ->
  209. {error, {prep_failed, LUser, LServer}};
  210. make_req(Method, Path, LUser, LServer, Password) ->
  211. AuthOpts = ejabberd_config:get_option({auth_opts, LServer}, fun(V) -> V end),
  212. BasicAuth = case lists:keyfind(basic_auth, 1, AuthOpts) of
  213. {_, BasicAuth0} -> BasicAuth0;
  214. _ -> ""
  215. end,
  216. PathPrefix = case lists:keyfind(path_prefix, 1, AuthOpts) of
  217. {_, Prefix} -> Prefix;
  218. false -> <<"/">>
  219. end,
  220. BasicAuth64 = base64:encode(BasicAuth),
  221. LUserE = list_to_binary(http_uri:encode(binary_to_list(LUser))),
  222. LServerE = list_to_binary(http_uri:encode(binary_to_list(LServer))),
  223. PasswordE = list_to_binary(http_uri:encode(binary_to_list(Password))),
  224. Query = <<"user=", LUserE/binary, "&server=", LServerE/binary, "&pass=", PasswordE/binary>>,
  225. Header = [{<<"Authorization">>, <<"Basic ", BasicAuth64/binary>>}],
  226. Connection = cuesport:get_worker(existing_pool_name(LServer)),
  227.  
  228. ?DEBUG("Making request '~s' for user ~s@~s...", [Path, LUser, LServer]),
  229. {ok, {{Code, _Reason}, _RespHeaders, RespBody, _, _}} = case Method of
  230. get -> fusco:request(Connection, <<PathPrefix/binary, Path/binary, "?", Query/binary>>,
  231. "GET", Header, "", 2, 5000);
  232. post -> fusco:request(Connection, <<PathPrefix/binary, Path/binary>>,
  233. "POST", Header, Query, 2, 5000)
  234. end,
  235.  
  236. ?DEBUG("Request result: ~s: ~p", [Code, RespBody]),
  237. case Code of
  238. <<"409">> -> {error, conflict};
  239. <<"404">> -> {error, not_found};
  240. <<"401">> -> {error, not_authorized};
  241. <<"403">> -> {error, not_allowed};
  242. <<"400">> -> {error, RespBody};
  243. <<"204">> -> {ok, <<"">>};
  244. <<"201">> -> {ok, created};
  245. <<"200">> -> {ok, RespBody}
  246. end.
  247.  
  248. %%%----------------------------------------------------------------------
  249. %%% Other internal functions
  250. %%%----------------------------------------------------------------------
  251. -spec pool_name(binary()) -> atom().
  252. pool_name(Host) ->
  253. list_to_atom("ejabberd_auth_http_" ++ binary_to_list(Host)).
  254.  
  255. -spec existing_pool_name(binary()) -> atom().
  256. existing_pool_name(Host) ->
  257. list_to_existing_atom("ejabberd_auth_http_" ++ binary_to_list(Host)).
  258.  
  259. -spec verify_scram_password(binary(), binary(), binary()) ->
  260. {ok, boolean()} | {error, bad_request | not_exists}.
  261. verify_scram_password(LUser, LServer, Password) ->
  262. case make_req(get, <<"get_password">>, LUser, LServer, <<"">>) of
  263. {ok, RawPassword} ->
  264. case scram2:deserialize(RawPassword) of
  265. {ok, #scram{} = ScramRecord} ->
  266. {ok, scram2:check_password(Password, ScramRecord)};
  267. _ ->
  268. {error, bad_request}
  269. end;
  270. _ ->
  271. {error, not_exists}
  272. end.
  273.  
  274. login(_User, _Server) ->
  275. erlang:error(not_implemented).
  276.  
  277. get_password(_User, _Server, _DefaultValue) ->
  278. erlang:error(not_implemented).
  279.  
  280. stop(Host) ->
  281. Proc = gen_mod:get_module_proc(Host, ?MODULE),
  282. supervisor:terminate_child(ejabberd_backend_sup, Proc),
  283. supervisor:delete_child(ejabberd_backend_sup, Proc).
  284.  
  285. %% -------------
  286.  
  287. is_user_exists(_User, _Server) -> true.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement