Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- %%%----------------------------------------------------------------------
- %%% File : ejabberd_auth_http.erl
- %%% Author : Piotr Nosek <piotr.nosek@erlang-solutions.com>
- %%% Purpose : Authentication via HTTP request
- %%% Created : 23 Sep 2013 by Piotr Nosek <piotr.nosek@erlang-solutions.com>
- %%%----------------------------------------------------------------------
- -module(ejabberd_auth_http).
- -author('piotr.nosek@erlang-solutions.com').
- -behaviour(ejabberd_auth).
- -behaviour(ejabberd_config).
- %% External exports
- -export([start/1,
- set_password/3,
- check_password/4,
- check_password/6,
- try_register/3,
- get_password/2,
- get_password_s/2,
- user_exists/2,
- remove_user/2,
- remove_user/3,
- plain_password_required/1,
- store_type/1,
- login/2,
- get_password/3,
- opt_type/1,
- stop/1]).
- -export([is_user_exists/2]).
- -include("ejabberd.hrl").
- -include("logger.hrl").
- %%%----------------------------------------------------------------------
- %%% API
- %%%----------------------------------------------------------------------
- opt_type(auth_opts) -> fun (V) -> V end;
- opt_type(_) -> [auth_opts].
- -spec start(binary()) -> ok.
- start(Host) ->
- AuthOpts = ejabberd_config:get_option({auth_opts, Host}, fun(V) -> V end),
- {_, AuthHost} = lists:keyfind(host, 1, AuthOpts),
- PoolSize = proplists:get_value(connection_pool_size, AuthOpts, 10),
- Opts = proplists:get_value(connection_opts, AuthOpts, []),
- ChildMods = [fusco],
- ChildMFA = {fusco, start_link, [binary_to_list(AuthHost), Opts]},
- Proc = gen_mod:get_module_proc(Host, ?MODULE),
- ChildSpec = {Proc, {cuesport, start_link,
- [pool_name(Host), PoolSize, ChildMods, ChildMFA]},
- transient, 2000, supervisor, [cuesport | ChildMods]},
- supervisor:start_child(ejabberd_backend_sup, ChildSpec).
- -spec plain_password_required(binary()) -> false.
- plain_password_required(Server) ->
- store_type(Server) == scram.
- -spec store_type(binary()) -> plain | scram.
- store_type(Server) ->
- ejabberd_auth:password_format(Server).
- -spec check_password(ejabberd:luser(), binary(), ejabberd:lserver(), binary()) -> boolean().
- check_password(LUser, _AuthzId, LServer, Password) ->
- case scram2:enabled(LServer) of
- false ->
- case make_req(get, <<"check_password">>, LUser, LServer, Password) of
- {ok, <<"true">>} -> true;
- _ -> false
- end;
- true ->
- {ok, true} =:= verify_scram_password(LUser, LServer, Password)
- end.
- -spec check_password(ejabberd:luser(), binary(), ejabberd:lserver(), binary(), binary(), fun()) -> boolean().
- check_password(LUser, _AuthzId, LServer, Password, Digest, DigestGen) ->
- case make_req(get, <<"get_password">>, LUser, LServer, <<"">>) of
- {error, _} ->
- false;
- {ok, GotPasswd} ->
- case scram2:enabled(LServer) of
- true ->
- case scram2:deserialize(GotPasswd) of
- {ok, #scram{} = Scram} ->
- scram2:check_digest(Scram, Digest, DigestGen, Password);
- _ ->
- false
- end;
- false ->
- check_digest(Digest, DigestGen, Password, GotPasswd)
- end
- end.
- -spec check_digest(binary(), fun(), binary(), binary()) -> boolean().
- check_digest(Digest, DigestGen, Password, Passwd) ->
- DigRes = if
- Digest /= <<>> ->
- Digest == DigestGen(Passwd);
- true ->
- false
- end,
- if DigRes ->
- true;
- true ->
- (Passwd == Password) and (Password /= <<>>)
- end.
- -spec set_password(ejabberd:luser(), ejabberd:lserver(), binary()) -> ok | {error, term()}.
- set_password(LUser, LServer, Password) ->
- PasswordFinal = case scram2:enabled(LServer) of
- true -> scram2:serialize(scram2:password_to_scram(
- Password, scram2:iterations(LServer)));
- false -> Password
- end,
- case make_req(post, <<"set_password">>, LUser, LServer, PasswordFinal) of
- {error, _} = Err -> Err;
- _ -> ok
- end.
- -spec try_register(ejabberd:luser(), ejabberd:lserver(), binary()) -> ok | {error, atom()}.
- try_register(LUser, LServer, Password) ->
- PasswordFinal = case scram2:enabled(LServer) of
- true -> scram2:serialize(scram2:password_to_scram(
- Password, scram2:iterations(LServer)));
- false -> Password
- end,
- case make_req(post, <<"register">>, LUser, LServer, PasswordFinal) of
- {ok, created} -> ok;
- {error, conflict} -> {error, exists};
- Error -> Error
- end.
- -spec get_password(ejabberd:luser(), ejabberd:lserver()) -> false | binary() |
- {binary(), binary(), binary(), integer()}.
- get_password(LUser, LServer) ->
- case make_req(get, <<"get_password">>, LUser, LServer, <<"">>) of
- {error, _} ->
- false;
- {ok, Password} ->
- case scram2:enabled(LServer) of
- true ->
- case scram2:deserialize(Password) of
- {ok, #scram{} = Scram} ->
- scram2:scram_to_tuple(Scram);
- _ ->
- false
- end;
- false ->
- Password
- end
- end.
- -spec get_password_s(ejabberd:luser(), ejabberd:lserver()) -> binary().
- get_password_s(User, Server) ->
- case get_password(User, Server) of
- Pass when is_binary(Pass) -> Pass;
- _ -> <<>>
- end.
- -spec user_exists(ejabberd:luser(), ejabberd:lserver()) -> boolean().
- user_exists(LUser, LServer) ->
- case make_req(get, <<"user_exists">>, LUser, LServer, <<"">>) of
- {ok, <<"true">>} -> true;
- _ -> false
- end.
- -spec remove_user(ejabberd:luser(), ejabberd:lserver()) -> ok | not_exists | not_allowed | bad_request.
- remove_user(LUser, LServer) ->
- remove_user_req(LUser, LServer, <<"">>, <<"remove_user">>).
- -spec remove_user(ejabberd:luser(), ejabberd:lserver(), binary()) -> ok | not_exists | not_allowed | bad_request.
- remove_user(LUser, LServer, Password) ->
- case scram2:enabled(LServer) of
- false ->
- remove_user_req(LUser, LServer, Password, <<"remove_user_validate">>);
- true ->
- case verify_scram_password(LUser, LServer, Password) of
- {ok, false} ->
- not_allowed;
- {ok, true} ->
- remove_user_req(LUser, LServer, <<"">>, <<"remove_user">>);
- {error, Error} ->
- Error
- end
- end.
- -spec remove_user_req(binary(), binary(), binary(), binary()) ->
- ok | not_exists | not_allowed | bad_request.
- remove_user_req(LUser, LServer, Password, Method) ->
- case make_req(post, Method, LUser, LServer, Password) of
- {error, not_allowed} -> not_allowed;
- {error, not_found} -> not_exists;
- {error, _} -> bad_request;
- _ -> ok
- end.
- %%%----------------------------------------------------------------------
- %%% Request maker
- %%%----------------------------------------------------------------------
- -spec make_req(post | get, binary(), binary(), binary(), binary()) ->
- {ok, Body :: binary()} | {error, term()}.
- make_req(_, _, LUser, LServer, _) when LUser == error orelse LServer == error ->
- {error, {prep_failed, LUser, LServer}};
- make_req(Method, Path, LUser, LServer, Password) ->
- AuthOpts = ejabberd_config:get_option({auth_opts, LServer}, fun(V) -> V end),
- BasicAuth = case lists:keyfind(basic_auth, 1, AuthOpts) of
- {_, BasicAuth0} -> BasicAuth0;
- _ -> ""
- end,
- PathPrefix = case lists:keyfind(path_prefix, 1, AuthOpts) of
- {_, Prefix} -> Prefix;
- false -> <<"/">>
- end,
- BasicAuth64 = base64:encode(BasicAuth),
- LUserE = list_to_binary(http_uri:encode(binary_to_list(LUser))),
- LServerE = list_to_binary(http_uri:encode(binary_to_list(LServer))),
- PasswordE = list_to_binary(http_uri:encode(binary_to_list(Password))),
- Query = <<"user=", LUserE/binary, "&server=", LServerE/binary, "&pass=", PasswordE/binary>>,
- Header = [{<<"Authorization">>, <<"Basic ", BasicAuth64/binary>>}],
- Connection = cuesport:get_worker(existing_pool_name(LServer)),
- ?DEBUG("Making request '~s' for user ~s@~s...", [Path, LUser, LServer]),
- {ok, {{Code, _Reason}, _RespHeaders, RespBody, _, _}} = case Method of
- get -> fusco:request(Connection, <<PathPrefix/binary, Path/binary, "?", Query/binary>>,
- "GET", Header, "", 2, 5000);
- post -> fusco:request(Connection, <<PathPrefix/binary, Path/binary>>,
- "POST", Header, Query, 2, 5000)
- end,
- ?DEBUG("Request result: ~s: ~p", [Code, RespBody]),
- case Code of
- <<"409">> -> {error, conflict};
- <<"404">> -> {error, not_found};
- <<"401">> -> {error, not_authorized};
- <<"403">> -> {error, not_allowed};
- <<"400">> -> {error, RespBody};
- <<"204">> -> {ok, <<"">>};
- <<"201">> -> {ok, created};
- <<"200">> -> {ok, RespBody}
- end.
- %%%----------------------------------------------------------------------
- %%% Other internal functions
- %%%----------------------------------------------------------------------
- -spec pool_name(binary()) -> atom().
- pool_name(Host) ->
- list_to_atom("ejabberd_auth_http_" ++ binary_to_list(Host)).
- -spec existing_pool_name(binary()) -> atom().
- existing_pool_name(Host) ->
- list_to_existing_atom("ejabberd_auth_http_" ++ binary_to_list(Host)).
- -spec verify_scram_password(binary(), binary(), binary()) ->
- {ok, boolean()} | {error, bad_request | not_exists}.
- verify_scram_password(LUser, LServer, Password) ->
- case make_req(get, <<"get_password">>, LUser, LServer, <<"">>) of
- {ok, RawPassword} ->
- case scram2:deserialize(RawPassword) of
- {ok, #scram{} = ScramRecord} ->
- {ok, scram2:check_password(Password, ScramRecord)};
- _ ->
- {error, bad_request}
- end;
- _ ->
- {error, not_exists}
- end.
- login(_User, _Server) ->
- erlang:error(not_implemented).
- get_password(_User, _Server, _DefaultValue) ->
- erlang:error(not_implemented).
- stop(Host) ->
- Proc = gen_mod:get_module_proc(Host, ?MODULE),
- supervisor:terminate_child(ejabberd_backend_sup, Proc),
- supervisor:delete_child(ejabberd_backend_sup, Proc).
- %% -------------
- is_user_exists(_User, _Server) -> true.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement