]> granicus.if.org Git - ejabberd/commitdiff
Merge commit 'refs/pull/524/head' of github.com:processone/ejabberd into sasl-api...
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Fri, 25 Mar 2016 15:16:50 +0000 (18:16 +0300)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Fri, 25 Mar 2016 15:16:50 +0000 (18:16 +0300)
18 files changed:
1  2 
src/cyrsasl.erl
src/cyrsasl_digest.erl
src/cyrsasl_plain.erl
src/ejabberd_auth.erl
src/ejabberd_auth_anonymous.erl
src/ejabberd_auth_external.erl
src/ejabberd_auth_internal.erl
src/ejabberd_auth_ldap.erl
src/ejabberd_auth_odbc.erl
src/ejabberd_auth_pam.erl
src/ejabberd_auth_riak.erl
src/ejabberd_c2s.erl
src/ejabberd_commands.erl
src/ejabberd_oauth.erl
src/ejabberd_web_admin.erl
src/mod_http_api.erl
src/mod_proxy65_stream.erl
src/mod_register_web.erl

diff --cc src/cyrsasl.erl
index cc03a49ce2b74739b939ac3a7d9d5adca631aba4,09b1a1a6f12d05a86783acc1c59b10f2c4921ed3..21fbc966016a6043072c64299bbb34aa64313a94
@@@ -132,8 -128,8 +132,8 @@@ register_mechanism(Mechanism, Module, P
  %%    end.
  
  check_credentials(_State, Props) ->
-     User = proplists:get_value(username, Props, <<>>),
+     User = proplists:get_value(authzid, Props, <<>>),
 -    case jlib:nodeprep(User) of
 +    case jid:nodeprep(User) of
        error -> {error, <<"not-authorized">>};
        <<"">> -> {error, <<"not-authorized">>};
        _LUser -> ok
Simple merge
index a9c1de056e7d3b3a165f3251d4ff921e0af4b22d,ceceacca867fcc598c73b1ddb0342678c58964ed..82d68f87fcd6cc646902631c36dc38eefe83bde7
@@@ -60,12 -60,17 +60,17 @@@ prepare(ClientIn) -
        [<<"">>, UserMaybeDomain, Password] ->
          case parse_domain(UserMaybeDomain) of
            %% <NUL>login@domain<NUL>pwd
-           [User, _Domain] -> [UserMaybeDomain, User, Password];
+           [User, _Domain] -> [User, User, Password];
            %% <NUL>login<NUL>pwd
-           [User] -> [<<"">>, User, Password]
+           [User] -> [User, User, Password]
          end;
 -        %% login@domain<NUL>login<NUL>pwd
+       [AuthzId, User, Password] ->
+       case parse_domain(AuthzId) of
-       [AuthzId, User, Password] -> [AuthzId, User, Password];
 +      %% login@domain<NUL>login<NUL>pwd
+         [AuthzUser, _Domain] -> [AuthzUser, User, Password];
+         %% login<NUL>login<NUL>pwd
+         [AuthzUser] -> [AuthzUser, User, Password]
+       end;
        _ -> error
      end.
  
index 2cc37c6e0ff2e83484e6308ddbbfd6c1ae49ec1e,bf47af85bffb1c71b33df77570c3e44cd21edd08..343ad94369494545f29851395932c32b51d3e7bf
@@@ -113,15 -111,15 +113,15 @@@ check_password(User, AuthzId, Server, P
      end.
  
  %% @doc Check if the user and password can login in server.
- %% @spec (User::string(), Server::string(), Password::string(),
+ %% @spec (User::string(), AuthzId::string(), Server::string(), Password::string(),
  %%        Digest::string(), DigestGen::function()) ->
  %%     true | false
- -spec check_password(binary(), binary(), binary(), binary(),
+ -spec check_password(binary(), binary(), binary(), binary(), binary(),
                       fun((binary()) -> binary())) -> boolean().
 -
 +                                 
- check_password(User, Server, Password, Digest,
+ check_password(User, AuthzId, Server, Password, Digest,
               DigestGen) ->
-     case check_password_with_authmodule(User, Server,
+     case check_password_with_authmodule(User, AuthzId, Server,
                                        Password, Digest, DigestGen)
        of
        {true, _AuthModule} -> true;
index 7b21752a26d1e8d86d0f6f2b73b1ac2aeba8c822,05f790db675fff9db772cedcb7acb78167ff47da..9c4b719c31080ce13c796512f7eb775c2134895b
         unregister_connection/3
        ]).
  
- -export([login/2, set_password/3, check_password/3,
-        check_password/5, try_register/3,
 -
 -%% Function used by ejabberd_auth:
+ -export([login/2, set_password/3, check_password/4,
+        check_password/6, try_register/3,
         dirty_get_registered_users/0, get_vh_registered_users/1,
 -         get_vh_registered_users/2, get_vh_registered_users_number/1,
 -         get_vh_registered_users_number/2, get_password_s/2,
 +       get_vh_registered_users/2,
 +       get_vh_registered_users_number/1,
 +       get_vh_registered_users_number/2, get_password_s/2,
         get_password/2, get_password/3, is_user_exists/2,
         remove_user/2, remove_user/3, store_type/0,
 -       plain_password_required/0]).
 +       plain_password_required/0, opt_type/1]).
  
  -include("ejabberd.hrl").
  -include("logger.hrl").
index 2944ac3f335e56383f9d45001b15cba7b42da7f0,44c931cbf091da1ac628f84b8d4a826930f9e9a0..5897fba5b732cb5c79098ee067fc09364c61e4aa
@@@ -31,8 -29,9 +31,8 @@@
  
  -behaviour(ejabberd_auth).
  
- -export([start/1, set_password/3, check_password/3,
-        check_password/5, try_register/3,
 -%% External exports
+ -export([start/1, set_password/3, check_password/4,
+        check_password/6, try_register/3,
         dirty_get_registered_users/0, get_vh_registered_users/1,
         get_vh_registered_users/2,
         get_vh_registered_users_number/1,
@@@ -76,16 -75,20 +76,20 @@@ plain_password_required() -> true
  
  store_type() -> external.
  
- check_password(User, Server, Password) ->
+ check_password(User, AuthzId, Server, Password) ->
+     if AuthzId /= <<>> andalso AuthzId /= User ->
+         false;
+     true ->
 -        case get_cache_option(Server) of
 +    case get_cache_option(Server) of
-       false -> check_password_extauth(User, Server, Password);
+           false -> check_password_extauth(User, AuthzId, Server, Password);
 -          {true, CacheTime} ->
 +      {true, CacheTime} ->
-         check_password_cache(User, Server, Password, CacheTime)
+         check_password_cache(User, AuthzId, Server, Password, CacheTime)
+         end
      end.
  
- check_password(User, Server, Password, _Digest,
+ check_password(User, AuthzId, Server, Password, _Digest,
               _DigestGen) ->
-     check_password(User, Server, Password).
+     check_password(User, AuthzId, Server, Password).
  
  set_password(User, Server, Password) ->
      case extauth:set_password(User, Server, Password) of
index d60e0fc5f9ae74502189a2f0bccba936f7bd557b,bb4ceab51928880f7cbb15a2baf77aa0381905ed..3b30b360735001689fc4632def338206505b51a0
@@@ -31,8 -29,9 +31,8 @@@
  
  -behaviour(ejabberd_auth).
  
- -export([start/1, set_password/3, check_password/3,
-        check_password/5, try_register/3,
 -%% External exports
+ -export([start/1, set_password/3, check_password/4,
+        check_password/6, try_register/3,
         dirty_get_registered_users/0, get_vh_registered_users/1,
         get_vh_registered_users/2,
         get_vh_registered_users_number/1,
@@@ -86,45 -85,53 +86,53 @@@ store_type() -
        true -> scram %% allows: PLAIN SCRAM
      end.
  
- check_password(User, Server, Password) ->
-     LUser = jid:nodeprep(User),
-     LServer = jid:nameprep(Server),
+ check_password(User, AuthzId, Server, Password) ->
+     if AuthzId /= <<>> andalso AuthzId /= User ->
+         false;
+     true ->
 -        LUser = jlib:nodeprep(User),
 -        LServer = jlib:nameprep(Server),
 -        US = {LUser, LServer},
 -        case catch mnesia:dirty_read({passwd, US}) of
 -          [#passwd{password = Password}]
 -        when is_binary(Password) ->
 -        Password /= <<"">>;
 -          [#passwd{password = Scram}]
 -        when is_record(Scram, scram) ->
 -        is_password_scram_valid(Password, Scram);
 -          _ -> false
++        LUser = jid:nodeprep(User),
++        LServer = jid:nameprep(Server),
 +    US = {LUser, LServer},
 +    case catch mnesia:dirty_read({passwd, US}) of
 +      [#passwd{password = Password}]
 +        when is_binary(Password) ->
 +        Password /= <<"">>;
 +      [#passwd{password = Scram}]
 +        when is_record(Scram, scram) ->
 +        is_password_scram_valid(Password, Scram);
 +      _ -> false
+         end
      end.
  
- check_password(User, Server, Password, Digest,
+ check_password(User, AuthzId, Server, Password, Digest,
               DigestGen) ->
-     LUser = jid:nodeprep(User),
-     LServer = jid:nameprep(Server),
+     if AuthzId /= <<>> andalso AuthzId /= User ->
+         false;
+     true ->
 -        LUser = jlib:nodeprep(User),
 -        LServer = jlib:nameprep(Server),
 -        US = {LUser, LServer},
 -        case catch mnesia:dirty_read({passwd, US}) of
 -          [#passwd{password = Passwd}] when is_binary(Passwd) ->
 -        DigRes = if Digest /= <<"">> ->
 -                        Digest == DigestGen(Passwd);
 -                    true -> false
 -                 end,
 -        if DigRes -> true;
 -           true -> (Passwd == Password) and (Password /= <<"">>)
 -        end;
 -          [#passwd{password = Scram}]
 -        when is_record(Scram, scram) ->
 -        Passwd = jlib:decode_base64(Scram#scram.storedkey),
 -        DigRes = if Digest /= <<"">> ->
 -                        Digest == DigestGen(Passwd);
 -                    true -> false
 -                 end,
 -        if DigRes -> true;
 -           true -> (Passwd == Password) and (Password /= <<"">>)
 -        end;
 -          _ -> false
++        LUser = jid:nodeprep(User),
++        LServer = jid:nameprep(Server),
 +    US = {LUser, LServer},
 +    case catch mnesia:dirty_read({passwd, US}) of
 +      [#passwd{password = Passwd}] when is_binary(Passwd) ->
 +        DigRes = if Digest /= <<"">> ->
 +                        Digest == DigestGen(Passwd);
 +                    true -> false
 +                 end,
 +        if DigRes -> true;
 +           true -> (Passwd == Password) and (Password /= <<"">>)
 +        end;
 +      [#passwd{password = Scram}]
 +        when is_record(Scram, scram) ->
 +        Passwd = jlib:decode_base64(Scram#scram.storedkey),
 +        DigRes = if Digest /= <<"">> ->
 +                        Digest == DigestGen(Passwd);
 +                    true -> false
 +                 end,
 +        if DigRes -> true;
 +           true -> (Passwd == Password) and (Password /= <<"">>)
 +        end;
 +      _ -> false
+         end
      end.
  
  %% @spec (User::string(), Server::string(), Password::string()) ->
index dd5d54a74a52ed18f34c5e8d18592c329a638782,45964d669b5f440013ca3cf33954fc04e12ddf79..51b466ef49f16fc0fb7fc41c64de5ff5b3cb6fbd
  -export([init/1, handle_info/2, handle_call/3,
         handle_cast/2, terminate/2, code_change/3]).
  
 -%% External exports
  -export([start/1, stop/1, start_link/1, set_password/3,
-        check_password/3, check_password/5, try_register/3,
+        check_password/4, check_password/6, try_register/3,
         dirty_get_registered_users/0, get_vh_registered_users/1,
 -         get_vh_registered_users/2,
 -         get_vh_registered_users_number/1,
 +       get_vh_registered_users/2,
 +       get_vh_registered_users_number/1,
         get_vh_registered_users_number/2, get_password/2,
         get_password_s/2, is_user_exists/2, remove_user/2,
 -       remove_user/3, store_type/0,
 -       plain_password_required/0]).
 +       remove_user/3, store_type/0, plain_password_required/0,
 +       opt_type/1]).
  
  -include("ejabberd.hrl").
  -include("logger.hrl").
@@@ -116,19 -115,23 +116,23 @@@ plain_password_required() -> true
  
  store_type() -> external.
  
- check_password(User, Server, Password) ->
+ check_password(User, AuthzId, Server, Password) ->
+     if AuthzId /= <<>> andalso AuthzId /= User ->
+         false;
+     true ->
 -        if Password == <<"">> -> false;
 -           true ->
 -         case catch check_password_ldap(User, Server, Password)
 -             of
 -           {'EXIT', _} -> false;
 -           Result -> Result
 -         end
 +    if Password == <<"">> -> false;
 +       true ->
 +         case catch check_password_ldap(User, Server, Password)
 +             of
 +           {'EXIT', _} -> false;
 +           Result -> Result
 +         end
+         end
      end.
  
- check_password(User, Server, Password, _Digest,
+ check_password(User, AuthzId, Server, Password, _Digest,
               _DigestGen) ->
-     check_password(User, Server, Password).
+     check_password(User, AuthzId, Server, Password).
  
  set_password(User, Server, Password) ->
      {ok, State} = eldap_utils:get_state(Server, ?MODULE),
index 18d0620129be78bd37568794c768073932f1a64c,b3bcd369ab0d72aeec424854cad774cd55becee8..dc3248fe8f6ddd09381a92873871394e6baba971
@@@ -31,8 -29,9 +31,8 @@@
  
  -behaviour(ejabberd_auth).
  
- -export([start/1, set_password/3, check_password/3,
-        check_password/5, try_register/3,
 -%% External exports
+ -export([start/1, set_password/3, check_password/4,
+        check_password/6, try_register/3,
         dirty_get_registered_users/0, get_vh_registered_users/1,
         get_vh_registered_users/2,
         get_vh_registered_users_number/1,
@@@ -63,84 -63,97 +63,92 @@@ store_type() -
        true -> scram %% allows: PLAIN SCRAM
      end.
  
- %% @spec (User, Server, Password) -> true | false | {error, Error}
- check_password(User, Server, Password) ->
-     LServer = jid:nameprep(Server),
-     LUser = jid:nodeprep(User),
+ %% @spec (User, AuthzId, Server, Password) -> true | false | {error, Error}
+ check_password(User, AuthzId, Server, Password) ->
+     if AuthzId /= <<>> andalso AuthzId /= User ->
+         false;
+     true ->
 -        LServer = jlib:nameprep(Server),
 -        LUser = jlib:nodeprep(User),
 -        if (LUser == error) or (LServer == error) ->
 -                false;
 -           (LUser == <<>>) or (LServer == <<>>) ->
 -                false;
 -           true ->
 -                Username = ejabberd_odbc:escape(LUser),
 -                case is_scrammed() of
 -                    true ->
 -                        try odbc_queries:get_password_scram(LServer, Username) of
 -                            {selected, [<<"password">>, <<"serverkey">>,
 -                                        <<"salt">>, <<"iterationcount">>],
 -                             [[StoredKey, ServerKey, Salt, IterationCount]]} ->
 -                                Scram =
 -                                    #scram{storedkey = StoredKey,
 -                                           serverkey = ServerKey,
 -                                           salt = Salt,
 -                                           iterationcount = jlib:binary_to_integer(
 -                                                              IterationCount)},
 -                                is_password_scram_valid(Password, Scram);
 -                            {selected, [<<"password">>, <<"serverkey">>,
 -                                        <<"salt">>, <<"iterationcount">>], []} ->
 -                                false; %% Account does not exist
 -                            {error, _Error} ->
 -                                false %% Typical error is that table doesn't exist
 -                        catch
 -                            _:_ ->
 -                                false %% Typical error is database not accessible
 -                        end;
 -                    false ->
 -                        try odbc_queries:get_password(LServer, Username) of
 -                            {selected, [<<"password">>], [[Password]]} ->
 -                                Password /= <<"">>;
 -                            {selected, [<<"password">>], [[_Password2]]} ->
 -                                false; %% Password is not correct
 -                            {selected, [<<"password">>], []} ->
 -                                false; %% Account does not exist
 -                            {error, _Error} ->
 -                                false %% Typical error is that table doesn't exist
 -                        catch
 -                            _:_ ->
 -                                false %% Typical error is database not accessible
 -                        end
 -                end
++        LServer = jid:nameprep(Server),
++        LUser = jid:nodeprep(User),
 +    if (LUser == error) or (LServer == error) ->
 +            false;
 +       (LUser == <<>>) or (LServer == <<>>) ->
 +            false;
 +       true ->
 +            case is_scrammed() of
 +                true ->
 +                    try odbc_queries:get_password_scram(LServer, LUser) of
 +                        {selected,
 +                         [{StoredKey, ServerKey, Salt, IterationCount}]} ->
 +                            Scram =
 +                                #scram{storedkey = StoredKey,
 +                                       serverkey = ServerKey,
 +                                       salt = Salt,
 +                                       iterationcount = IterationCount},
 +                            is_password_scram_valid(Password, Scram);
 +                        {selected, []} ->
 +                            false; %% Account does not exist
 +                        {error, _Error} ->
 +                            false %% Typical error is that table doesn't exist
 +                    catch
 +                        _:_ ->
 +                            false %% Typical error is database not accessible
 +                    end;
 +                false ->
 +                    try odbc_queries:get_password(LServer, LUser) of
 +                        {selected, [{Password}]} ->
 +                            Password /= <<"">>;
 +                        {selected, [{_Password2}]} ->
 +                            false; %% Password is not correct
 +                        {selected, []} ->
 +                            false; %% Account does not exist
 +                        {error, _Error} ->
 +                            false %% Typical error is that table doesn't exist
 +                    catch
 +                        _:_ ->
 +                            false %% Typical error is database not accessible
 +                    end
 +            end
+         end
      end.
  
- %% @spec (User, Server, Password, Digest, DigestGen) -> true | false | {error, Error}
- check_password(User, Server, Password, Digest,
+ %% @spec (User, AuthzId, Server, Password, Digest, DigestGen) -> true | false | {error, Error}
+ check_password(User, AuthzId, Server, Password, Digest,
               DigestGen) ->
-     LServer = jid:nameprep(Server),
-     LUser = jid:nodeprep(User),
+     if AuthzId /= <<>> andalso AuthzId /= User ->
+         false;
+     true ->
 -        LServer = jlib:nameprep(Server),
 -        LUser = jlib:nodeprep(User),
 -        if (LUser == error) or (LServer == error) ->
 -                false;
 -           (LUser == <<>>) or (LServer == <<>>) ->
 -                false;
 -           true ->
 -                case is_scrammed() of
 -                    false ->
 -                        Username = ejabberd_odbc:escape(LUser),
 -                        try odbc_queries:get_password(LServer, Username) of
 -                            %% Account exists, check if password is valid
 -                            {selected, [<<"password">>], [[Passwd]]} ->
 -                                DigRes = if Digest /= <<"">> ->
 -                                                 Digest == DigestGen(Passwd);
 -                                            true -> false
 -                                         end,
 -                                if DigRes -> true;
 -                                   true -> (Passwd == Password) and (Password /= <<"">>)
 -                                end;
 -                            {selected, [<<"password">>], []} ->
 -                                false; %% Account does not exist
 -                            {error, _Error} ->
 -                                false %% Typical error is that table doesn't exist
 -                        catch
 -                            _:_ ->
 -                                false %% Typical error is database not accessible
 -                        end;
 -                    true ->
 -                        false
 -                end
++        LServer = jid:nameprep(Server),
++        LUser = jid:nodeprep(User),
 +    if (LUser == error) or (LServer == error) ->
 +            false;
 +       (LUser == <<>>) or (LServer == <<>>) ->
 +            false;
 +       true ->
 +            case is_scrammed() of
 +                false ->
 +                    try odbc_queries:get_password(LServer, LUser) of
 +                        %% Account exists, check if password is valid
 +                        {selected, [{Passwd}]} ->
 +                            DigRes = if Digest /= <<"">> ->
 +                                             Digest == DigestGen(Passwd);
 +                                        true -> false
 +                                     end,
 +                            if DigRes -> true;
 +                               true -> (Passwd == Password) and (Password /= <<"">>)
 +                            end;
 +                        {selected, []} ->
 +                            false; %% Account does not exist
 +                        {error, _Error} ->
 +                            false %% Typical error is that table doesn't exist
 +                    catch
 +                        _:_ ->
 +                            false %% Typical error is database not accessible
 +                    end;
 +                true ->
 +                    false
 +            end
+         end
      end.
  
  %% @spec (User::string(), Server::string(), Password::string()) ->
index ee5123c5eb930b8b8e5cc4ebcacbb820fdccc11f,da893eb4674b4e55b37a4480fe70f9577e63c512..fa4b9f078f43e954a524285ba1eaa1b146439478
  
  -behaviour(ejabberd_auth).
  
- -export([start/1, set_password/3, check_password/3,
-        check_password/5, try_register/3,
 -%% External exports
 -%%====================================================================
 -%% API
 -%%====================================================================
+ -export([start/1, set_password/3, check_password/4,
+        check_password/6, try_register/3,
         dirty_get_registered_users/0, get_vh_registered_users/1,
 -         get_vh_registered_users/2, get_vh_registered_users_number/1,
 -         get_vh_registered_users_number/2,
 -       get_password/2, get_password_s/2, is_user_exists/2,
 -       remove_user/2, remove_user/3, store_type/0,
 -       plain_password_required/0]).
 +       get_vh_registered_users/2,
 +       get_vh_registered_users_number/1,
 +       get_vh_registered_users_number/2, get_password/2,
 +       get_password_s/2, is_user_exists/2, remove_user/2,
 +       remove_user/3, store_type/0, plain_password_required/0,
 +       opt_type/1]).
  
  start(_Host) ->
      ejabberd:start_app(p1_pam).
  set_password(_User, _Server, _Password) ->
      {error, not_allowed}.
  
- check_password(User, Server, Password, _Digest,
+ check_password(User, AuthzId, Server, Password, _Digest,
               _DigestGen) ->
-     check_password(User, Server, Password).
+     check_password(User, AuthzId, Server, Password).
  
- check_password(User, Host, Password) ->
+ check_password(User, AuthzId, Host, Password) ->
+     if AuthzId /= <<>> andalso AuthzId /= User ->
+         false;
+     true ->
 -        Service = get_pam_service(Host),
 -        UserInfo = case get_pam_userinfotype(Host) of
 -               username -> User;
 -               jid -> <<User/binary, "@", Host/binary>>
 -             end,
 -        case catch epam:authenticate(Service, UserInfo,
 -                               Password)
 -      of
 -          true -> true;
 -          _ -> false
 +    Service = get_pam_service(Host),
 +    UserInfo = case get_pam_userinfotype(Host) of
 +               username -> User;
 +               jid -> <<User/binary, "@", Host/binary>>
 +             end,
 +    case catch epam:authenticate(Service, UserInfo,
 +                               Password)
 +      of
 +      true -> true;
 +      _ -> false
+         end
      end.
  
  try_register(_User, _Server, _Password) ->
index 64bf12c6aa2639487ee2f749762130678ca867c8,8c926f4bd6c9778f0bb502ca631c3161ef00ff36..bc745fea449870b4d7ad2065064e54e55124f690
@@@ -66,42 -66,50 +66,50 @@@ store_type() -
  passwd_schema() ->
      {record_info(fields, passwd), #passwd{}}.
  
- check_password(User, Server, Password) ->
-     LUser = jid:nodeprep(User),
-     LServer = jid:nameprep(Server),
+ check_password(User, AuthzId, Server, Password) ->
+     if AuthzId /= <<>> andalso AuthzId /= User ->
+         false;
+     true ->
 -        LUser = jlib:nodeprep(User),
 -        LServer = jlib:nameprep(Server),
 -        case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
 -            {ok, #passwd{password = Password}} when is_binary(Password) ->
 -                Password /= <<"">>;
 -            {ok, #passwd{password = Scram}} when is_record(Scram, scram) ->
 -                is_password_scram_valid(Password, Scram);
 -            _ ->
 -                false
++        LUser = jid:nodeprep(User),
++        LServer = jid:nameprep(Server),
 +    case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
 +        {ok, #passwd{password = Password}} when is_binary(Password) ->
 +            Password /= <<"">>;
 +        {ok, #passwd{password = Scram}} when is_record(Scram, scram) ->
 +            is_password_scram_valid(Password, Scram);
 +        _ ->
 +            false
+         end
      end.
  
- check_password(User, Server, Password, Digest,
+ check_password(User, AuthzId, Server, Password, Digest,
               DigestGen) ->
-     LUser = jid:nodeprep(User),
-     LServer = jid:nameprep(Server),
+     if AuthzId /= <<>> andalso AuthzId /= User ->
+         false;
+     true ->
 -        LUser = jlib:nodeprep(User),
 -        LServer = jlib:nameprep(Server),
 -        case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
 -          {ok, #passwd{password = Passwd}} when is_binary(Passwd) ->
 -        DigRes = if Digest /= <<"">> ->
 -                        Digest == DigestGen(Passwd);
 -                    true -> false
 -                 end,
 -        if DigRes -> true;
 -           true -> (Passwd == Password) and (Password /= <<"">>)
 -        end;
 -          {ok, #passwd{password = Scram}}
 -        when is_record(Scram, scram) ->
 -        Passwd = jlib:decode_base64(Scram#scram.storedkey),
 -        DigRes = if Digest /= <<"">> ->
 -                        Digest == DigestGen(Passwd);
 -                    true -> false
 -                 end,
 -        if DigRes -> true;
 -           true -> (Passwd == Password) and (Password /= <<"">>)
 -        end;
 -          _ -> false
++        LUser = jid:nodeprep(User),
++        LServer = jid:nameprep(Server),
 +    case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
 +      {ok, #passwd{password = Passwd}} when is_binary(Passwd) ->
 +        DigRes = if Digest /= <<"">> ->
 +                        Digest == DigestGen(Passwd);
 +                    true -> false
 +                 end,
 +        if DigRes -> true;
 +           true -> (Passwd == Password) and (Password /= <<"">>)
 +        end;
 +      {ok, #passwd{password = Scram}}
 +        when is_record(Scram, scram) ->
 +        Passwd = jlib:decode_base64(Scram#scram.storedkey),
 +        DigRes = if Digest /= <<"">> ->
 +                        Digest == DigestGen(Passwd);
 +                    true -> false
 +                 end,
 +        if DigRes -> true;
 +           true -> (Passwd == Password) and (Password /= <<"">>)
 +        end;
 +      _ -> false
+         end
      end.
  
  set_password(User, Server, Password) ->
index d58c1e1d2349a6bf94b383ca52547587916d2b92,22e17bee0de10ef0928e5f20d56384cc4300818e..936abc7a968caf6bc19c22833e76562d5ff6c307
@@@ -376,180 -390,168 +376,180 @@@ wait_for_stream({xmlstreamstart, _Name
                                    TLS = StateData#state.tls,
                                    TLSEnabled = StateData#state.tls_enabled,
                                    TLSRequired = StateData#state.tls_required,
 -                                  SASLState =
 -                                      cyrsasl:server_new(
 -                                        <<"jabber">>, Server, <<"">>, [],
 -                                        fun(U) ->
 -                                                ejabberd_auth:get_password_with_authmodule(
 -                                                  U, Server)
 -                                        end,
 +                                  SASLState = cyrsasl:server_new(
 +                                          <<"jabber">>, Server, <<"">>, [],
 +                                          fun (U) ->
 +                                                  ejabberd_auth:get_password_with_authmodule(
 +                                                      U, Server)
 +                                          end,
-                                           fun (U, P) ->
+                                         fun(U, AuthzId, P) ->
 -                                                ejabberd_auth:check_password_with_authmodule(
 +                                                  ejabberd_auth:check_password_with_authmodule(
-                                                       U, Server, P)
+                                                   U, AuthzId, Server, P)
 -                                        end,
 +                                          end,
-                                           fun (U, P, D, DG) ->
+                                         fun(U, AuthzId, P, D, DG) ->
 -                                                ejabberd_auth:check_password_with_authmodule(
 +                                                  ejabberd_auth:check_password_with_authmodule(
-                                                       U, Server, P, D, DG)
+                                                   U, AuthzId, Server, P, D, DG)
 -                                        end),
 +                                          end),
                                    Mechs =
                                        case TLSEnabled or not TLSRequired of
 -                                          true ->
 -                                              Ms = lists:map(fun (S) ->
 -                                                                     #xmlel{name = <<"mechanism">>,
 -                                                                            attrs = [],
 -                                                                            children = [{xmlcdata, S}]}
 -                                                             end,
 -                                                             cyrsasl:listmech(Server)),
 -                                              [#xmlel{name = <<"mechanisms">>,
 -                                                      attrs = [{<<"xmlns">>, ?NS_SASL}],
 -                                                      children = Ms}];
 -                                          false ->
 -                                              []
 -                                      end,
 +                                      true ->
 +                                          Ms = lists:map(fun (S) ->
 +                                                          #xmlel{name = <<"mechanism">>,
 +                                                              attrs = [],
 +                                                              children = [{xmlcdata, S}]}
 +                                                  end,
 +                                                  cyrsasl:listmech(Server)),
 +                                          [#xmlel{name = <<"mechanisms">>,
 +                                                  attrs = [{<<"xmlns">>, ?NS_SASL}],
 +                                                  children = Ms}];
 +                                      false ->
 +                                          []
 +                                  end,
                                    SockMod =
 -                                      (StateData#state.sockmod):get_sockmod(
 -                                        StateData#state.socket),
 +                                      (StateData#state.sockmod):get_sockmod(StateData#state.socket),
                                    Zlib = StateData#state.zlib,
 -                                  CompressFeature =
 -                                      case Zlib andalso
 -                                          ((SockMod == gen_tcp) orelse
 -                                           (SockMod == p1_tls)) of
 -                                          true ->
 -                                              [#xmlel{name = <<"compression">>,
 -                                                      attrs = [{<<"xmlns">>, ?NS_FEATURE_COMPRESS}],
 -                                                      children = [#xmlel{name = <<"method">>,
 -                                                                  attrs = [],
 -                                                                  children = [{xmlcdata, <<"zlib">>}]}]}];
 -                                          _ ->
 -                                              []
 -                                      end,
 +                                  CompressFeature = case Zlib andalso
 +                                      ((SockMod == gen_tcp) orelse (SockMod == fast_tls)) of
 +                                      true ->
 +                                          [#xmlel{name = <<"compression">>,
 +                                                  attrs = [{<<"xmlns">>, ?NS_FEATURE_COMPRESS}],
 +                                                  children = [#xmlel{name = <<"method">>,
 +                                                          attrs = [],
 +                                                          children = [{xmlcdata, <<"zlib">>}]}]}];
 +                                      _ ->
 +                                          []
 +                                  end,
                                    TLSFeature =
                                        case (TLS == true) andalso
 -                                          (TLSEnabled == false) andalso
 -                                          (SockMod == gen_tcp) of
 -                                          true ->
 -                                              case TLSRequired of
 -                                                  true ->
 -                                                      [#xmlel{name = <<"starttls">>,
 -                                                              attrs = [{<<"xmlns">>, ?NS_TLS}],
 -                                                              children = [#xmlel{name = <<"required">>,
 -                                                                          attrs = [],
 -                                                                          children = []}]}];
 -                                                  _ ->
 -                                                      [#xmlel{name = <<"starttls">>,
 -                                                              attrs = [{<<"xmlns">>, ?NS_TLS}],
 -                                                              children = []}]
 -                                              end;
 -                                          false ->
 -                                              []
 -                                      end,
 +                                      (TLSEnabled == false) andalso
 +                                      (SockMod == gen_tcp) of
 +                                      true ->
 +                                          case TLSRequired of
 +                                              true ->
 +                                                  [#xmlel{name = <<"starttls">>,
 +                                                          attrs = [{<<"xmlns">>, ?NS_TLS}],
 +                                                          children = [#xmlel{name = <<"required">>,
 +                                                                  attrs = [],
 +                                                                  children = []}]}];
 +                                              _ ->
 +                                                  [#xmlel{name = <<"starttls">>,
 +                                                          attrs = [{<<"xmlns">>, ?NS_TLS}],
 +                                                          children = []}]
 +                                          end;
 +                                      false ->
 +                                          []
 +                                  end,
 +                                  StreamFeatures1 = TLSFeature ++ CompressFeature ++ Mechs,
 +                                  StreamFeatures = ejabberd_hooks:run_fold(c2s_stream_features,
 +                                          Server, StreamFeatures1, [Server]),
                                    send_element(StateData,
 -                                          #xmlel{name = <<"stream:features">>,
 -                                                 attrs = [],
 -                                                 children =
 -                                                  TLSFeature ++ CompressFeature ++ Mechs
 -                                                  ++
 -                                                  ejabberd_hooks:run_fold(c2s_stream_features,
 -                                                      Server, [], [Server])}),
 +                                      #xmlel{name = <<"stream:features">>,
 +                                          attrs = [],
 +                                          children = StreamFeatures}),
                                    fsm_next_state(wait_for_feature_request,
 -                                             StateData#state{
 -                                               server = Server,
 -                                               sasl_state = SASLState,
 -                                               lang = Lang});
 +                                      StateData#state{server = Server,
 +                                          sasl_state = SASLState,
 +                                          lang = Lang});
                                _ ->
                                    case StateData#state.resource of
 -                                  <<"">> ->
 -                                      RosterVersioningFeature =
 -                                          ejabberd_hooks:run_fold(roster_get_versioning_feature,
 -                                                                  Server, [],
 -                                                                  [Server]),
 -                                      StreamManagementFeature =
 -                                          case stream_mgmt_enabled(StateData) of
 -                                            true ->
 -                                                [#xmlel{name = <<"sm">>,
 -                                                        attrs = [{<<"xmlns">>, ?NS_STREAM_MGMT_2}],
 -                                                        children = []},
 -                                                 #xmlel{name = <<"sm">>,
 -                                                        attrs = [{<<"xmlns">>, ?NS_STREAM_MGMT_3}],
 -                                                        children = []}];
 -                                            false ->
 -                                                []
 +                                      <<"">> ->
 +                                          RosterVersioningFeature =
 +                                              ejabberd_hooks:run_fold(roster_get_versioning_feature,
 +                                                  Server, [],
 +                                                  [Server]),
 +                                          StreamManagementFeature =
 +                                              case stream_mgmt_enabled(StateData) of
 +                                              true ->
 +                                                  [#xmlel{name = <<"sm">>,
 +                                                          attrs = [{<<"xmlns">>, ?NS_STREAM_MGMT_2}],
 +                                                          children = []},
 +                                                      #xmlel{name = <<"sm">>,
 +                                                          attrs = [{<<"xmlns">>, ?NS_STREAM_MGMT_3}],
 +                                                          children = []}];
 +                                              false ->
 +                                                  []
                                            end,
 -                                      StreamFeatures = [#xmlel{name = <<"bind">>,
 -                                                              attrs = [{<<"xmlns">>, ?NS_BIND}],
 -                                                              children = []},
 -                                                          #xmlel{name = <<"session">>,
 -                                                              attrs = [{<<"xmlns">>, ?NS_SESSION}],
 -                                                              children = []}]
 -                                                          ++
 -                                                          RosterVersioningFeature ++
 -                                                          StreamManagementFeature ++
 -                                                          ejabberd_hooks:run_fold(c2s_post_auth_features,
 -                                                              Server, [], [Server]) ++
 -                                                          ejabberd_hooks:run_fold(c2s_stream_features,
 -                                                              Server, [], [Server]),
 -                                      send_element(StateData,
 -                                                  #xmlel{name = <<"stream:features">>,
 -                                                          attrs = [],
 -                                                          children = StreamFeatures}),
 -                                      fsm_next_state(wait_for_bind,
 -                                                      StateData#state{server = Server, lang = Lang});
 -                                  _ ->
 -                                      send_element(StateData,
 -                                                  #xmlel{name = <<"stream:features">>,
 -                                                          attrs = [],
 -                                                          children = []}),
 -                                      fsm_next_state(wait_for_session,
 -                                                      StateData#state{server = Server, lang = Lang})
 +                                          SockMod =
 +                                              (StateData#state.sockmod):get_sockmod(
 +                                                StateData#state.socket),
 +                                          Zlib = StateData#state.zlib,
 +                                          CompressFeature =
 +                                              case Zlib andalso
 +                                                  ((SockMod == gen_tcp) orelse (SockMod == fast_tls)) of
 +                                                  true ->
 +                                                      [#xmlel{name = <<"compression">>,
 +                                                              attrs = [{<<"xmlns">>, ?NS_FEATURE_COMPRESS}],
 +                                                              children = [#xmlel{name = <<"method">>,
 +                                                                                 attrs = [],
 +                                                                                 children = [{xmlcdata, <<"zlib">>}]}]}];
 +                                                  _ ->
 +                                                      []
 +                                              end,
 +                                          StreamFeatures1 = [#xmlel{name = <<"bind">>,
 +                                                      attrs = [{<<"xmlns">>, ?NS_BIND}],
 +                                                      children = []},
 +                                                  #xmlel{name = <<"session">>,
 +                                                      attrs = [{<<"xmlns">>, ?NS_SESSION}],
 +                                                      children =
 +                                                           [#xmlel{name = <<"optional">>}]}]
 +                                              ++
 +                                              RosterVersioningFeature ++
 +                                              StreamManagementFeature ++
 +                                              CompressFeature ++
 +                                              ejabberd_hooks:run_fold(c2s_post_auth_features,
 +                                                  Server, [], [Server]),
 +                                          StreamFeatures = ejabberd_hooks:run_fold(c2s_stream_features,
 +                                                  Server, StreamFeatures1, [Server]),
 +                                          send_element(StateData,
 +                                              #xmlel{name = <<"stream:features">>,
 +                                                  attrs = [],
 +                                                  children = StreamFeatures}),
 +                                          fsm_next_state(wait_for_bind,
 +                                              StateData#state{server = Server, lang = Lang});
 +                                      _ ->
 +                                          send_element(StateData,
 +                                              #xmlel{name = <<"stream:features">>,
 +                                                  attrs = [],
 +                                                  children = []}),
 +                                          fsm_next_state(session_established,
 +                                              StateData#state{server = Server, lang = Lang})
                                    end
                            end;
 -              _ ->
 -                  send_header(StateData, Server, <<"">>, DefaultLang),
 -                  if not StateData#state.tls_enabled and
 -                      StateData#state.tls_required ->
 -                          send_element(StateData,
 +                      _ ->
 +                          send_header(StateData, Server, <<"">>, DefaultLang),
 +                          if not StateData#state.tls_enabled and
 +                                      StateData#state.tls_required ->
 +                                  send_element(StateData,
                                        ?POLICY_VIOLATION_ERR(Lang,
 -                                                          <<"Use of STARTTLS required">>)),
 -                          send_trailer(StateData),
 -                          {stop, normal, StateData};
 -                      true ->
 -                          fsm_next_state(wait_for_auth,
 +                                          <<"Use of STARTTLS required">>)),
 +                                  send_trailer(StateData),
 +                                  {stop, normal, StateData};
 +                              true ->
 +                                  fsm_next_state(wait_for_auth,
                                        StateData#state{server = Server,
 -                                                      lang = Lang})
 -                  end
 +                                          lang = Lang})
 +                          end
 +                  end;
 +              true ->
 +                  IP = StateData#state.ip,
 +                  {true, LogReason, ReasonT} = IsBlacklistedIP,
 +                  ?INFO_MSG("Connection attempt from blacklisted IP ~s: ~s",
 +                      [jlib:ip_to_list(IP), LogReason]),
 +                  send_header(StateData, Server, <<"">>, DefaultLang),
 +                  send_element(StateData, ?POLICY_VIOLATION_ERR(Lang, ReasonT)),
 +                  send_trailer(StateData),
 +                  {stop, normal, StateData};
 +              _ ->
 +                  send_header(StateData, ?MYNAME, <<"">>, DefaultLang),
 +                  send_element(StateData, ?HOST_UNKNOWN_ERR),
 +                  send_trailer(StateData),
 +                  {stop, normal, StateData}
            end;
 -      true ->
 -              IP = StateData#state.ip,
 -              {true, LogReason, ReasonT} = IsBlacklistedIP,
 -              ?INFO_MSG("Connection attempt from blacklisted IP ~s: ~s",
 -                        [jlib:ip_to_list(IP), LogReason]),
 -              send_header(StateData, Server, <<"">>, DefaultLang),
 -              send_element(StateData, ?POLICY_VIOLATION_ERR(Lang, ReasonT)),
 -              send_trailer(StateData),
 -              {stop, normal, StateData};
        _ ->
            send_header(StateData, ?MYNAME, <<"">>, DefaultLang),
 -          send_element(StateData, ?HOST_UNKNOWN_ERR),
 +          send_element(StateData, ?INVALID_NS_ERR),
            send_trailer(StateData),
            {stop, normal, StateData}
 -      end;
 -    _ ->
 -      send_header(StateData, ?MYNAME, <<"">>, DefaultLang),
 -      send_element(StateData, ?INVALID_NS_ERR),
 -      send_trailer(StateData),
 -      {stop, normal, StateData}
      end;
  wait_for_stream(timeout, StateData) ->
      {stop, normal, StateData};
@@@ -576,144 -576,147 +576,144 @@@ wait_for_auth({xmlstreamelement, #xmlel
      fsm_next_state(wait_for_auth, dispatch_stream_mgmt(El, StateData));
  wait_for_auth({xmlstreamelement, El}, StateData) ->
      case is_auth_packet(El) of
 -      {auth, _ID, get, {U, _, _, _}} ->
 -        #xmlel{name = Name, attrs = Attrs} =
 -            jlib:make_result_iq_reply(El),
 -        case U of
 -          <<"">> -> UCdata = [];
 -          _ -> UCdata = [{xmlcdata, U}]
 -        end,
 -        Res = case
 -                ejabberd_auth:plain_password_required(StateData#state.server)
 -                  of
 -                false ->
 -                    #xmlel{name = Name, attrs = Attrs,
 -                           children =
 -                               [#xmlel{name = <<"query">>,
 -                                       attrs = [{<<"xmlns">>, ?NS_AUTH}],
 -                                       children =
 -                                           [#xmlel{name = <<"username">>,
 -                                                   attrs = [],
 -                                                   children = UCdata},
 -                                            #xmlel{name = <<"password">>,
 -                                                   attrs = [], children = []},
 -                                            #xmlel{name = <<"digest">>,
 -                                                   attrs = [], children = []},
 -                                            #xmlel{name = <<"resource">>,
 -                                                   attrs = [],
 -                                                   children = []}]}]};
 -                true ->
 -                    #xmlel{name = Name, attrs = Attrs,
 -                           children =
 -                               [#xmlel{name = <<"query">>,
 -                                       attrs = [{<<"xmlns">>, ?NS_AUTH}],
 -                                       children =
 -                                           [#xmlel{name = <<"username">>,
 -                                                   attrs = [],
 -                                                   children = UCdata},
 -                                            #xmlel{name = <<"password">>,
 -                                                   attrs = [], children = []},
 -                                            #xmlel{name = <<"resource">>,
 -                                                   attrs = [],
 -                                                   children = []}]}]}
 -              end,
 -        send_element(StateData, Res),
 -        fsm_next_state(wait_for_auth, StateData);
 -      {auth, _ID, set, {_U, _P, _D, <<"">>}} ->
 -        Err = jlib:make_error_reply(El,
 -                                    ?ERR_AUTH_NO_RESOURCE_PROVIDED((StateData#state.lang))),
 -        send_element(StateData, Err),
 -        fsm_next_state(wait_for_auth, StateData);
 -      {auth, _ID, set, {U, P, D, R}} ->
 -        JID = jlib:make_jid(U, StateData#state.server, R),
 -        case JID /= error andalso
 -               acl:match_rule(StateData#state.server,
 -                              StateData#state.access, JID)
 -                 == allow
 -            of
 -          true ->
 -              DGen = fun (PW) ->
 -                             p1_sha:sha(<<(StateData#state.streamid)/binary, PW/binary>>)
 -                     end,
 +      {auth, _ID, get, {U, _, _, _}} ->
 +          #xmlel{name = Name, attrs = Attrs} = jlib:make_result_iq_reply(El),
 +          case U of
 +              <<"">> -> UCdata = [];
 +              _ -> UCdata = [{xmlcdata, U}]
 +          end,
 +          Res = case
 +              ejabberd_auth:plain_password_required(StateData#state.server)
 +          of
 +              false ->
 +                  #xmlel{name = Name, attrs = Attrs,
 +                      children =
 +                      [#xmlel{name = <<"query">>,
 +                              attrs = [{<<"xmlns">>, ?NS_AUTH}],
 +                              children =
 +                              [#xmlel{name = <<"username">>,
 +                                      attrs = [],
 +                                      children = UCdata},
 +                                  #xmlel{name = <<"password">>,
 +                                      attrs = [], children = []},
 +                                  #xmlel{name = <<"digest">>,
 +                                      attrs = [], children = []},
 +                                  #xmlel{name = <<"resource">>,
 +                                      attrs = [],
 +                                      children = []}]}]};
 +              true ->
 +                  #xmlel{name = Name, attrs = Attrs,
 +                      children =
 +                      [#xmlel{name = <<"query">>,
 +                              attrs = [{<<"xmlns">>, ?NS_AUTH}],
 +                              children =
 +                              [#xmlel{name = <<"username">>,
 +                                      attrs = [],
 +                                      children = UCdata},
 +                                  #xmlel{name = <<"password">>,
 +                                      attrs = [], children = []},
 +                                  #xmlel{name = <<"resource">>,
 +                                      attrs = [],
 +                                      children = []}]}]}
 +          end,
 +          send_element(StateData, Res),
 +          fsm_next_state(wait_for_auth, StateData);
 +      {auth, _ID, set, {_U, _P, _D, <<"">>}} ->
 +          Err = jlib:make_error_reply(El,
 +                  ?ERR_AUTH_NO_RESOURCE_PROVIDED((StateData#state.lang))),
 +          send_element(StateData, Err),
 +          fsm_next_state(wait_for_auth, StateData);
 +      {auth, _ID, set, {U, P, D, R}} ->
 +          JID = jid:make(U, StateData#state.server, R),
 +          case JID /= error andalso
 +              acl:match_rule(StateData#state.server,
 +                  StateData#state.access, JID)
 +              == allow
 +          of
 +              true ->
 +                  DGen = fun (PW) ->
 +                          p1_sha:sha(<<(StateData#state.streamid)/binary, PW/binary>>)
 +                  end,
-                   case ejabberd_auth:check_password_with_authmodule(U,
+               case ejabberd_auth:check_password_with_authmodule(U, U,
 -                                                                StateData#state.server,
 -                                                                P, D, DGen)
 +                          StateData#state.server,
 +                          P, D, DGen)
                    of
 -                {true, AuthModule} ->
 -                      ?INFO_MSG("(~w) Accepted legacy authentication for ~s by ~p from ~s",
 -                                [StateData#state.socket,
 -                                 jlib:jid_to_string(JID), AuthModule,
 -                                 jlib:ip_to_list(StateData#state.ip)]),
 -                      ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
 -                                         [true, U, StateData#state.server,
 -                                          StateData#state.ip]),
 -                      Conn = get_conn_type(StateData),
 -                      Info = [{ip, StateData#state.ip}, {conn, Conn},
 +                      {true, AuthModule} ->
 +                          ?INFO_MSG("(~w) Accepted legacy authentication for ~s by ~p from ~s",
 +                              [StateData#state.socket,
 +                                  jid:to_string(JID), AuthModule,
 +                                  ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]),
 +                          ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
 +                              [true, U, StateData#state.server,
 +                                  StateData#state.ip]),
 +                          Conn = get_conn_type(StateData),
 +                          Info = [{ip, StateData#state.ip}, {conn, Conn},
                                    {auth_module, AuthModule}],
 -                        Res = jlib:make_result_iq_reply(
 -                                El#xmlel{children = []}),
 -                      send_element(StateData, Res),
 -                      ejabberd_sm:open_session(StateData#state.sid, U,
 -                                               StateData#state.server, R,
 -                                               Info),
 -                      change_shaper(StateData, JID),
 -                      {Fs, Ts} =
 -                          ejabberd_hooks:run_fold(roster_get_subscription_lists,
 -                                                  StateData#state.server,
 -                                                  {[], []},
 -                                                  [U,
 -                                                      StateData#state.server]),
 -                      LJID =
 -                          jlib:jid_tolower(jlib:jid_remove_resource(JID)),
 -                      Fs1 = [LJID | Fs],
 -                      Ts1 = [LJID | Ts],
 -                      PrivList = ejabberd_hooks:run_fold(privacy_get_user_list,
 -                                                  StateData#state.server,
 -                                                  #userlist{},
 -                                                  [U, StateData#state.server]),
 -                      NewStateData = StateData#state{user = U,
 -                                                      resource = R,
 -                                                      jid = JID,
 -                                                      conn = Conn,
 -                                                      auth_module = AuthModule,
 -                                                      pres_f = (?SETS):from_list(Fs1),
 -                                                      pres_t = (?SETS):from_list(Ts1),
 -                                                      privacy_list = PrivList},
 -                      fsm_next_state(session_established, NewStateData);
 -                _ ->
 -                      ?INFO_MSG("(~w) Failed legacy authentication for ~s from ~s",
 -                                [StateData#state.socket,
 -                                 jlib:jid_to_string(JID),
 -                                 jlib:ip_to_list(StateData#state.ip)]),
 -                    ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
 -                                       [false, U, StateData#state.server,
 -                                        StateData#state.ip]),
 -                    Err = jlib:make_error_reply(El, ?ERR_NOT_AUTHORIZED),
 -                    send_element(StateData, Err),
 -                    fsm_next_state(wait_for_auth, StateData)
 -              end;
 -          _ ->
 -              if JID == error ->
 -                     ?INFO_MSG("(~w) Forbidden legacy authentication "
 -                               "for username '~s' with resource '~s'",
 -                               [StateData#state.socket, U, R]),
 -                     Err = jlib:make_error_reply(El, ?ERR_JID_MALFORMED),
 -                     send_element(StateData, Err),
 -                     fsm_next_state(wait_for_auth, StateData);
 -                 true ->
 -                     ?INFO_MSG("(~w) Forbidden legacy authentication "
 -                               "for ~s from ~s",
 -                               [StateData#state.socket,
 -                                jlib:jid_to_string(JID),
 -                                jlib:ip_to_list(StateData#state.ip)]),
 -                     ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
 -                                        [false, U, StateData#state.server,
 -                                         StateData#state.ip]),
 -                     Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
 -                     send_element(StateData, Err),
 -                     fsm_next_state(wait_for_auth, StateData)
 -              end
 -        end;
 -      _ ->
 -        process_unauthenticated_stanza(StateData, El),
 -        fsm_next_state(wait_for_auth, StateData)
 +                          Res = jlib:make_result_iq_reply(
 +                                  El#xmlel{children = []}),
 +                          send_element(StateData, Res),
 +                          ejabberd_sm:open_session(StateData#state.sid, U,
 +                              StateData#state.server, R,
 +                              Info),
 +                          change_shaper(StateData, JID),
 +                          {Fs, Ts} =
 +                              ejabberd_hooks:run_fold(roster_get_subscription_lists,
 +                                  StateData#state.server,
 +                                  {[], []},
 +                                  [U, StateData#state.server]),
 +                          LJID = jid:tolower(jid:remove_resource(JID)),
 +                          Fs1 = [LJID | Fs],
 +                          Ts1 = [LJID | Ts],
 +                          PrivList = ejabberd_hooks:run_fold(privacy_get_user_list,
 +                                  StateData#state.server,
 +                                  #userlist{},
 +                                  [U, StateData#state.server]),
 +                          NewStateData = StateData#state{user = U,
 +                                  resource = R,
 +                                  jid = JID,
 +                                  conn = Conn,
 +                                  auth_module = AuthModule,
 +                                  pres_f = (?SETS):from_list(Fs1),
 +                                  pres_t = (?SETS):from_list(Ts1),
 +                                  privacy_list = PrivList},
 +                          fsm_next_state(session_established, NewStateData);
 +                      _ ->
 +                          ?INFO_MSG("(~w) Failed legacy authentication for ~s from ~s",
 +                              [StateData#state.socket,
 +                                  jid:to_string(JID),
 +                                  ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]),
 +                          ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
 +                              [false, U, StateData#state.server,
 +                                  StateData#state.ip]),
 +                          Err = jlib:make_error_reply(El, ?ERR_NOT_AUTHORIZED),
 +                          send_element(StateData, Err),
 +                          fsm_next_state(wait_for_auth, StateData)
 +                  end;
 +              _ ->
 +                  if JID == error ->
 +                          ?INFO_MSG("(~w) Forbidden legacy authentication "
 +                              "for username '~s' with resource '~s'",
 +                              [StateData#state.socket, U, R]),
 +                          Err = jlib:make_error_reply(El, ?ERR_JID_MALFORMED),
 +                          send_element(StateData, Err),
 +                          fsm_next_state(wait_for_auth, StateData);
 +                      true ->
 +                          ?INFO_MSG("(~w) Forbidden legacy authentication "
 +                              "for ~s from ~s",
 +                              [StateData#state.socket,
 +                                  jid:to_string(JID),
 +                                  ejabberd_config:may_hide_data(jlib:ip_to_list(StateData#state.ip))]),
 +                          ejabberd_hooks:run(c2s_auth_result, StateData#state.server,
 +                              [false, U, StateData#state.server,
 +                                  StateData#state.ip]),
 +                          Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
 +                          send_element(StateData, Err),
 +                          fsm_next_state(wait_for_auth, StateData)
 +                  end
 +          end;
 +      _ ->
 +          process_unauthenticated_stanza(StateData, El),
 +          fsm_next_state(wait_for_auth, StateData)
      end;
  wait_for_auth(timeout, StateData) ->
      {stop, normal, StateData};
@@@ -3129,14 -3121,8 +3123,20 @@@ pack_string(String, Pack) -
  transform_listen_option(Opt, Opts) ->
      [Opt|Opts].
  
 -      case proplists:get_value(authzid, Props, <<>>) of
 -              <<>> -> proplists:get_value(username, Props, <<>>);
 -              AuthzId -> AuthzId
 -      end.
+ identity(Props) ->
++    case proplists:get_value(authzid, Props, <<>>) of
++      <<>> -> proplists:get_value(username, Props, <<>>);
++      AuthzId -> AuthzId
++    end.
++
 +opt_type(domain_certfile) -> fun iolist_to_binary/1;
 +opt_type(max_fsm_queue) ->
 +    fun (I) when is_integer(I), I > 0 -> I end;
 +opt_type(resource_conflict) ->
 +    fun (setresource) -> setresource;
 +      (closeold) -> closeold;
 +      (closenew) -> closenew;
 +      (acceptnew) -> acceptnew
 +    end;
 +opt_type(_) ->
 +    [domain_certfile, max_fsm_queue, resource_conflict].
index 265d7141fdd5dd7a938ea36021148c62d601e503,ca40d5dc31c0d4cce129339b0ab19180ffa33bbe..3c98316da490bbf6dbcb5b705845c89029cdcb13
@@@ -500,53 -390,31 +500,53 @@@ check_access_commands(AccessCommands, A
        L when is_list(L) -> ok
      end.
  
 --spec check_auth(noauth) -> noauth_provided;
 -                ({binary(), binary(), binary()}) -> {ok, binary(), binary()}.
 +-spec check_auth(ejabberd_commands(), noauth) -> noauth_provided;
 +                (ejabberd_commands(),
 +                 {binary(), binary(), binary(), boolean()}) ->
 +    {ok, binary(), binary()}.
  
 -check_auth(noauth) ->
 +check_auth(_Command, noauth) ->
      no_auth_provided;
 -check_auth({User, Server, Password}) ->
 +check_auth(Command, {User, Server, {oauth, Token}, _}) ->
 +    Scope = erlang:atom_to_binary(Command#ejabberd_commands.name, utf8),
 +    case ejabberd_oauth:check_token(User, Server, Scope, Token) of
 +        true ->
 +            {ok, User, Server};
 +        false ->
 +            throw({error, invalid_account_data})
 +    end;
 +check_auth(_Command, {User, Server, Password, _}) when is_binary(Password) ->
      %% Check the account exists and password is valid
-     case ejabberd_auth:check_password(User, Server, Password) of
+     case ejabberd_auth:check_password(User, <<"">>, Server, Password) of
 -      true -> {ok, User, Server};
 -      _ -> throw({error, invalid_account_data})
 +        true -> {ok, User, Server};
 +        _ -> throw({error, invalid_account_data})
      end.
  
 -check_access(all, _) ->
 +check_access(Command, ?POLICY_ACCESS, _)
 +  when Command#ejabberd_commands.policy == open ->
      true;
 -check_access(Access, Auth) ->
 -    case check_auth(Auth) of
 +check_access(_Command, _Access, admin) ->
 +    true;
 +check_access(_Command, _Access, {_User, _Server, _, true}) ->
 +    false;
 +check_access(Command, Access, Auth)
 +  when Access =/= ?POLICY_ACCESS;
 +       Command#ejabberd_commands.policy == open;
 +       Command#ejabberd_commands.policy == user ->
 +    case check_auth(Command, Auth) of
        {ok, User, Server} ->
 -          check_access(Access, User, Server);
 +          check_access2(Access, User, Server);
        _ ->
            false
 -    end.
 +    end;
 +check_access(_Command, _Access, _Auth) ->
 +    false.
  
 -check_access(Access, User, Server) ->
 +check_access2(?POLICY_ACCESS, _User, _Server) ->
 +    true;
 +check_access2(Access, User, Server) ->
      %% Check this user has access permission
 -    case acl:match_rule(Server, Access, jlib:make_jid(User, Server, <<"">>)) of
 +    case acl:match_rule(Server, Access, jid:make(User, Server, <<"">>)) of
        allow -> true;
        deny -> false
      end.
index 1925a2f78e057484d45e843a373b9eaf52098d6f,0000000000000000000000000000000000000000..d8e0a435d12711451065ba0320021e9b7d5f8c94
mode 100644,000000..100644
--- /dev/null
@@@ -1,490 -1,0 +1,490 @@@
-                     case ejabberd_auth:check_password(User, Server, Password) of
 +%%%-------------------------------------------------------------------
 +%%% File    : ejabberd_oauth.erl
 +%%% Author  : Alexey Shchepin <alexey@process-one.net>
 +%%% Purpose : OAUTH2 support
 +%%% Created : 20 Mar 2015 by Alexey Shchepin <alexey@process-one.net>
 +%%%
 +%%%
 +%%% ejabberd, Copyright (C) 2002-2016   ProcessOne
 +%%%
 +%%% This program is free software; you can redistribute it and/or
 +%%% modify it under the terms of the GNU General Public License as
 +%%% published by the Free Software Foundation; either version 2 of the
 +%%% License, or (at your option) any later version.
 +%%%
 +%%% This program is distributed in the hope that it will be useful,
 +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
 +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 +%%% General Public License for more details.
 +%%%
 +%%% You should have received a copy of the GNU General Public License
 +%%% along with this program; if not, write to the Free Software
 +%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 +%%% 02111-1307 USA
 +%%%
 +%%%-------------------------------------------------------------------
 +
 +-module(ejabberd_oauth).
 +
 +-behaviour(gen_server).
 +
 +%% gen_server callbacks
 +-export([init/1, handle_call/3, handle_cast/2,
 +       handle_info/2, terminate/2, code_change/3]).
 +
 +-export([start/0,
 +         start_link/0,
 +         get_client_identity/2,
 +         verify_redirection_uri/3,
 +         authenticate_user/2,
 +         authenticate_client/2,
 +         verify_resowner_scope/3,
 +         associate_access_code/3,
 +         associate_access_token/3,
 +         associate_refresh_token/3,
 +         check_token/4,
 +         check_token/2,
 +         process/2,
 +         opt_type/1]).
 +
 +-include("jlib.hrl").
 +
 +-include("ejabberd.hrl").
 +-include("logger.hrl").
 +
 +-include("ejabberd_http.hrl").
 +-include("ejabberd_web_admin.hrl").
 +
 +-record(oauth_token, {
 +          token = {<<"">>, <<"">>} :: {binary(), binary()},
 +          us = {<<"">>, <<"">>}    :: {binary(), binary()},
 +          scope = []               :: [binary()],
 +          expire                   :: integer()
 +         }).
 +
 +-define(EXPIRE, 3600).
 +
 +start() ->
 +    init_db(mnesia, ?MYNAME),
 +    Expire = expire(),
 +    application:set_env(oauth2, backend, ejabberd_oauth),
 +    application:set_env(oauth2, expiry_time, Expire),
 +    application:start(oauth2),
 +    ChildSpec = {?MODULE, {?MODULE, start_link, []},
 +               temporary, 1000, worker, [?MODULE]},
 +    supervisor:start_child(ejabberd_sup, ChildSpec),
 +    ok.
 +
 +start_link() ->
 +    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
 +
 +
 +init([]) ->
 +    erlang:send_after(expire() * 1000, self(), clean),
 +    {ok, ok}.
 +
 +handle_call(_Request, _From, State) ->
 +    {reply, bad_request, State}.
 +
 +handle_cast(_Msg, State) -> {noreply, State}.
 +
 +handle_info(clean, State) ->
 +    {MegaSecs, Secs, MiniSecs} = os:timestamp(),
 +    TS = 1000000 * MegaSecs + Secs,
 +    F = fun() ->
 +              Ts = mnesia:select(
 +                     oauth_token,
 +                     [{#oauth_token{expire = '$1', _ = '_'},
 +                       [{'<', '$1', TS}],
 +                       ['$_']}]),
 +              lists:foreach(fun mnesia:delete_object/1, Ts)
 +        end,
 +    mnesia:async_dirty(F),
 +    erlang:send_after(trunc(expire() * 1000 * (1 + MiniSecs / 1000000)),
 +                      self(), clean),
 +    {noreply, State};
 +handle_info(_Info, State) -> {noreply, State}.
 +
 +terminate(_Reason, _State) -> ok.
 +
 +code_change(_OldVsn, State, _Extra) -> {ok, State}.
 +
 +
 +init_db(mnesia, _Host) ->
 +    mnesia:create_table(oauth_token,
 +                        [{disc_copies, [node()]},
 +                         {attributes,
 +                          record_info(fields, oauth_token)}]),
 +    mnesia:add_table_copy(oauth_token, node(), disc_copies);
 +init_db(_, _) ->
 +    ok.
 +
 +
 +get_client_identity(Client, Ctx) -> {ok, {Ctx, {client, Client}}}.
 +
 +verify_redirection_uri(_, _, Ctx) -> {ok, Ctx}.
 +
 +authenticate_user({User, Server}, {password, Password} = Ctx) ->
 +    case jid:make(User, Server, <<"">>) of
 +        #jid{} = JID ->
 +            Access =
 +                ejabberd_config:get_option(
 +                  {oauth_access, JID#jid.lserver},
 +                  fun(A) when is_atom(A) -> A end,
 +                  none),
 +            case acl:match_rule(JID#jid.lserver, Access, JID) of
 +                allow ->
++                    case ejabberd_auth:check_password(User, <<"">>, Server, Password) of
 +                        true ->
 +                            {ok, {Ctx, {user, User, Server}}};
 +                        false ->
 +                            {error, badpass}
 +                    end;
 +                deny ->
 +                    {error, badpass}
 +            end;
 +        error ->
 +            {error, badpass}
 +    end.
 +
 +authenticate_client(Client, Ctx) -> {ok, {Ctx, {client, Client}}}.
 +
 +verify_resowner_scope({user, _User, _Server}, Scope, Ctx) ->
 +    Cmds = ejabberd_commands:get_commands(),
 +    Cmds1 = [sasl_auth | Cmds],
 +    RegisteredScope = [atom_to_binary(C, utf8) || C <- Cmds1],
 +    case oauth2_priv_set:is_subset(oauth2_priv_set:new(Scope),
 +                                   oauth2_priv_set:new(RegisteredScope)) of
 +        true ->
 +            {ok, {Ctx, Scope}};
 +        false ->
 +            {error, badscope}
 +    end;
 +verify_resowner_scope(_, _, _) ->
 +    {error, badscope}.
 +
 +
 +associate_access_code(_AccessCode, _Context, AppContext) ->
 +    %put(?ACCESS_CODE_TABLE, AccessCode, Context),
 +    {ok, AppContext}.
 +
 +associate_access_token(AccessToken, Context, AppContext) ->
 +    {user, User, Server} =
 +        proplists:get_value(<<"resource_owner">>, Context, <<"">>),
 +    Scope = proplists:get_value(<<"scope">>, Context, []),
 +    Expire = proplists:get_value(<<"expiry_time">>, Context, 0),
 +    LUser = jid:nodeprep(User),
 +    LServer = jid:nameprep(Server),
 +    R = #oauth_token{
 +      token = AccessToken,
 +      us = {LUser, LServer},
 +      scope = Scope,
 +      expire = Expire
 +     },
 +    mnesia:dirty_write(R),
 +    {ok, AppContext}.
 +
 +associate_refresh_token(_RefreshToken, _Context, AppContext) ->
 +    %put(?REFRESH_TOKEN_TABLE, RefreshToken, Context),
 +    {ok, AppContext}.
 +
 +
 +check_token(User, Server, Scope, Token) ->
 +    LUser = jid:nodeprep(User),
 +    LServer = jid:nameprep(Server),
 +    case catch mnesia:dirty_read(oauth_token, Token) of
 +        [#oauth_token{us = {LUser, LServer},
 +                      scope = TokenScope,
 +                      expire = Expire}] ->
 +            {MegaSecs, Secs, _} = os:timestamp(),
 +            TS = 1000000 * MegaSecs + Secs,
 +            oauth2_priv_set:is_member(
 +              Scope, oauth2_priv_set:new(TokenScope)) andalso
 +                Expire > TS;
 +        _ ->
 +            false
 +    end.
 +
 +check_token(Scope, Token) ->
 +    case catch mnesia:dirty_read(oauth_token, Token) of
 +        [#oauth_token{us = {LUser, LServer},
 +                      scope = TokenScope,
 +                      expire = Expire}] ->
 +            {MegaSecs, Secs, _} = os:timestamp(),
 +            TS = 1000000 * MegaSecs + Secs,
 +            case oauth2_priv_set:is_member(
 +                   Scope, oauth2_priv_set:new(TokenScope)) andalso
 +                Expire > TS of
 +                true -> {ok, LUser, LServer};
 +                false -> false
 +            end;
 +        _ ->
 +            false
 +    end.
 +
 +
 +expire() ->
 +    ejabberd_config:get_option(
 +      oauth_expire,
 +      fun(I) when is_integer(I) -> I end,
 +      ?EXPIRE).
 +
 +-define(DIV(Class, Els),
 +      ?XAE(<<"div">>, [{<<"class">>, Class}], Els)).
 +-define(INPUTID(Type, Name, Value),
 +      ?XA(<<"input">>,
 +          [{<<"type">>, Type}, {<<"name">>, Name},
 +           {<<"value">>, Value}, {<<"id">>, Name}])).
 +-define(LABEL(ID, Els),
 +      ?XAE(<<"label">>, [{<<"for">>, ID}], Els)).
 +
 +process(_Handlers,
 +      #request{method = 'GET', q = Q, lang = Lang,
 +               path = [_, <<"authorization_token">>]}) ->
 +    ResponseType = proplists:get_value(<<"response_type">>, Q, <<"">>),
 +    ClientId = proplists:get_value(<<"client_id">>, Q, <<"">>),
 +    RedirectURI = proplists:get_value(<<"redirect_uri">>, Q, <<"">>),
 +    Scope = proplists:get_value(<<"scope">>, Q, <<"">>),
 +    State = proplists:get_value(<<"state">>, Q, <<"">>),
 +    Form =
 +        ?XAE(<<"form">>,
 +             [{<<"action">>, <<"authorization_token">>},
 +              {<<"method">>, <<"post">>}],
 +             [?LABEL(<<"username">>, [?CT(<<"User">>), ?C(<<": ">>)]),
 +              ?INPUTID(<<"text">>, <<"username">>, <<"">>),
 +              ?BR,
 +              ?LABEL(<<"server">>, [?CT(<<"Server">>), ?C(<<": ">>)]),
 +              ?INPUTID(<<"text">>, <<"server">>, <<"">>),
 +              ?BR,
 +              ?LABEL(<<"password">>, [?CT(<<"Password">>), ?C(<<": ">>)]),
 +              ?INPUTID(<<"password">>, <<"password">>, <<"">>),
 +              ?INPUT(<<"hidden">>, <<"response_type">>, ResponseType),
 +              ?INPUT(<<"hidden">>, <<"client_id">>, ClientId),
 +              ?INPUT(<<"hidden">>, <<"redirect_uri">>, RedirectURI),
 +              ?INPUT(<<"hidden">>, <<"scope">>, Scope),
 +              ?INPUT(<<"hidden">>, <<"state">>, State),
 +              ?BR,
 +              ?INPUTT(<<"submit">>, <<"">>, <<"Accept">>)
 +             ]),
 +    Top =
 +        ?DIV(<<"section">>,
 +             [?DIV(<<"block">>,
 +                   [?A(<<"https://www.ejabberd.im">>,
 +                       [?XA(<<"img">>,
 +                            [{<<"height">>, <<"32">>},
 +                             {<<"src">>, logo()}])]
 +                      )])]),
 +    Middle =
 +        ?DIV(<<"white section">>,
 +             [?DIV(<<"block">>,
 +                   [?XC(<<"h1">>, <<"Authorization request">>),
 +                    ?XE(<<"p">>,
 +                        [?C(<<"Application ">>),
 +                         ?XC(<<"em">>, ClientId),
 +                         ?C(<<" wants to access scope ">>),
 +                         ?XC(<<"em">>, Scope)]),
 +                    Form
 +                   ])]),
 +    Bottom =
 +        ?DIV(<<"section">>,
 +             [?DIV(<<"block">>,
 +                   [?XAC(<<"a">>,
 +                         [{<<"href">>, <<"https://www.ejabberd.im">>},
 +                          {<<"title">>, <<"ejabberd XMPP server">>}],
 +                         <<"ejabberd">>),
 +                    ?C(" is maintained by "),
 +                    ?XAC(<<"a">>,
 +                         [{<<"href">>, <<"https://www.process-one.net">>},
 +                          {<<"title">>, <<"ProcessOne - Leader in Instant Messaging and Push Solutions">>}],
 +                         <<"ProcessOne">>)
 +                   ])]),
 +    Body = ?DIV(<<"container">>, [Top, Middle, Bottom]),
 +    ejabberd_web:make_xhtml(web_head(), [Body]);
 +process(_Handlers,
 +      #request{method = 'POST', q = Q, lang = _Lang,
 +               path = [_, <<"authorization_token">>]}) ->
 +    _ResponseType = proplists:get_value(<<"response_type">>, Q, <<"">>),
 +    ClientId = proplists:get_value(<<"client_id">>, Q, <<"">>),
 +    RedirectURI = proplists:get_value(<<"redirect_uri">>, Q, <<"">>),
 +    SScope = proplists:get_value(<<"scope">>, Q, <<"">>),
 +    Username = proplists:get_value(<<"username">>, Q, <<"">>),
 +    Server = proplists:get_value(<<"server">>, Q, <<"">>),
 +    Password = proplists:get_value(<<"password">>, Q, <<"">>),
 +    State = proplists:get_value(<<"state">>, Q, <<"">>),
 +    Scope = str:tokens(SScope, <<" ">>),
 +    case oauth2:authorize_password({Username, Server},
 +                                   ClientId,
 +                                   RedirectURI,
 +                                   Scope,
 +                                   {password, Password}) of
 +        {ok, {_AppContext, Authorization}} ->
 +            {ok, {_AppContext2, Response}} =
 +                oauth2:issue_token(Authorization, none),
 +            {ok, AccessToken} = oauth2_response:access_token(Response),
 +            {ok, Type} = oauth2_response:token_type(Response),
 +            {ok, Expires} = oauth2_response:expires_in(Response),
 +            {ok, VerifiedScope} = oauth2_response:scope(Response),
 +            %oauth2_wrq:redirected_access_token_response(ReqData,
 +            %                                            RedirectURI,
 +            %                                            AccessToken,
 +            %                                            Type,
 +            %                                            Expires,
 +            %                                            VerifiedScope,
 +            %                                            State,
 +            %                                            Context);
 +            {302, [{<<"Location">>,
 +                    <<RedirectURI/binary,
 +                     "?access_token=", AccessToken/binary,
 +                     "&token_type=", Type/binary,
 +                     "&expires_in=", (integer_to_binary(Expires))/binary,
 +                     "&scope=", (str:join(VerifiedScope, <<" ">>))/binary,
 +                     "&state=", State/binary>>
 +                   }],
 +             ejabberd_web:make_xhtml([?XC(<<"h1">>, <<"302 Found">>)])};
 +        {error, Error} when is_atom(Error) ->
 +            %oauth2_wrq:redirected_error_response(
 +            %    ReqData, RedirectURI, Error, State, Context)
 +            {302, [{<<"Location">>,
 +                    <<RedirectURI/binary,
 +                     "?error=", (atom_to_binary(Error, utf8))/binary,
 +                     "&state=", State/binary>>
 +                   }],
 +             ejabberd_web:make_xhtml([?XC(<<"h1">>, <<"302 Found">>)])}
 +    end;
 +process(_Handlers, _Request) ->
 +    ejabberd_web:error(not_found).
 +
 +
 +
 +
 +web_head() ->
 +    [?XA(<<"meta">>, [{<<"http-equiv">>, <<"X-UA-Compatible">>},
 +                      {<<"content">>, <<"IE=edge">>}]),
 +     ?XA(<<"meta">>, [{<<"name">>, <<"viewport">>},
 +                      {<<"content">>,
 +                       <<"width=device-width, initial-scale=1">>}]),
 +     ?XC(<<"title">>, <<"Authorization request">>),
 +     ?XC(<<"style">>, css())
 +    ].
 +
 +css() ->
 +    <<"
 +      body {
 +        margin: 0;
 +        padding: 0;
 +
 +        font-family: sans-serif;
 +        color: #fff;
 +      }
 +
 +      h1 {
 +        font-size: 3em;
 +        color: #444;
 +      }
 +
 +      p {
 +        line-height: 1.5em;
 +        color: #888;
 +      }
 +
 +      a {
 +        color: #fff;
 +      }
 +        a:hover,
 +        a:active {
 +          text-decoration: underline;
 +        }
 +
 +      em {
 +        display: inline-block;
 +        padding: 0 5px;
 +
 +        background: #f4f4f4;
 +        border-radius: 5px;
 +
 +        font-style: normal;
 +        font-weight: bold;
 +        color: #444;
 +      }
 +
 +      form {
 +        color: #444;
 +      }
 +        label {
 +          display: block;
 +          font-weight: bold;
 +        }
 +
 +        input[type=text],
 +        input[type=password] {
 +          margin-bottom: 1em;
 +          padding: 0.4em;
 +
 +          max-width: 330px;
 +          width: 100%;
 +
 +          border: 1px solid #c4c4c4;
 +          border-radius: 5px;
 +          outline: 0;
 +
 +          font-size: 1.2em;
 +        }
 +          input[type=text]:focus,
 +          input[type=password]:focus,
 +          input[type=text]:active,
 +          input[type=password]:active {
 +            border-color: #41AFCA;
 +          }
 +
 +        input[type=submit] {
 +          font-size: 1em;
 +        }
 +
 +      .container {
 +        position: absolute;
 +        top: 0;
 +        left: 0;
 +        right: 0;
 +        bottom: 0;
 +
 +        background: #424A55;
 +        background-image: -webkit-linear-gradient(270deg, rgba(48,52,62,0) 24%, #30353e 100%);
 +        background-image: linear-gradient(-180deg, rgba(48,52,62,0) 24%, #30353e 100%);
 +      }
 +
 +      .section {
 +        padding: 3em;
 +      }
 +        .white.section {
 +          background: #fff;
 +          border-bottom: 4px solid #41AFCA;
 +        }
 +
 +        .white.section a {
 +          text-decoration: none;
 +          color: #41AFCA;
 +        }
 +          .white.section a:hover,
 +          .white.section a:active {
 +            text-decoration: underline;
 +          }
 +
 +      .container > .section { 
 +          background: #424A55;
 +      }
 +
 +      .block {
 +        margin: 0 auto;
 +        max-width: 900px;
 +        width: 100%;
 +      }
 +">>.
 +
 +logo() ->
 +    <<"data:image/png;base64,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">>.
 +
 +opt_type(oauth_expire) ->
 +    fun(I) when is_integer(I), I >= 0 -> I end;
 +opt_type(oauth_access) ->
 +    fun(A) when is_atom(A) -> A end;
 +opt_type(_) -> [oauth_expire, oauth_access].
index 11d90ab80dba41af310c26cb9d88e34d30152609,7cf15210cc86483f6fcf07ce2fb63096c062c7ef..f525a4d39b5be82157b678caa556e95841c74462
@@@ -264,10 -263,10 +264,10 @@@ get_auth_admin(Auth, HostHTTP, RPath, M
  
  get_auth_account(HostOfRule, AccessRule, User, Server,
                 Pass) ->
-     case ejabberd_auth:check_password(User, Server, Pass) of
+     case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of
        true ->
          case is_acl_match(HostOfRule, AccessRule,
 -                          jlib:make_jid(User, Server, <<"">>))
 +                          jid:make(User, Server, <<"">>))
              of
            false -> {unauthorized, <<"unprivileged-account">>};
            true -> {ok, {User, Server}}
index 7f86d5cc1820e7a5aaaccd6b652b7e036d4f483a,0000000000000000000000000000000000000000..3b7a09cb78a3f31ecc7ac85bc23cd294affc1e45
mode 100644,000000..100644
--- /dev/null
@@@ -1,392 -1,0 +1,392 @@@
-                                 case ejabberd_auth:check_password(User, Server, Pass) of
 +%%%----------------------------------------------------------------------
 +%%% File    : mod_http_api.erl
 +%%% Author  : Christophe romain <christophe.romain@process-one.net>
 +%%% Purpose : Implements REST API for ejabberd using JSON data
 +%%% Created : 15 Sep 2014 by Christophe Romain <christophe.romain@process-one.net>
 +%%%
 +%%%
 +%%% ejabberd, Copyright (C) 2002-2016   ProcessOne
 +%%%
 +%%% This program is free software; you can redistribute it and/or
 +%%% modify it under the terms of the GNU General Public License as
 +%%% published by the Free Software Foundation; either version 2 of the
 +%%% License, or (at your option) any later version.
 +%%%
 +%%% This program is distributed in the hope that it will be useful,
 +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
 +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 +%%% General Public License for more details.
 +%%%
 +%%% You should have received a copy of the GNU General Public License along
 +%%% with this program; if not, write to the Free Software Foundation, Inc.,
 +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 +%%%
 +%%%----------------------------------------------------------------------
 +
 +%% Example config:
 +%%
 +%%  in ejabberd_http listener
 +%%    request_handlers:
 +%%      "/api": mod_http_api
 +%% 
 +%% Access rights are defined with:
 +%% commands_admin_access: configure 
 +%% commands:
 +%%   - add_commands: user
 +%%
 +%%
 +%% add_commands allow exporting a class of commands, from
 +%%   open: methods is not risky and can be called by without any access check
 +%%   restricted (default): the same, but will appear only in ejabberdctl list.
 +%%   admin – auth is required with XMLRPC and HTTP API and checked for admin priviledges, works as usual in ejabberdctl.
 +%%   user - can be used through XMLRPC and HTTP API, even by user. Only admin can use the commands for other users.
 +%%
 +%% Then to perform an action, send a POST request to the following URL:
 +%% http://localhost:5280/api/<call_name>
 +
 +-module(mod_http_api).
 +
 +-author('cromain@process-one.net').
 +
 +-behaviour(gen_mod).
 +
 +-export([start/2, stop/1, process/2, mod_opt_type/1]).
 +
 +-include("ejabberd.hrl").
 +-include("jlib.hrl").
 +-include("logger.hrl").
 +-include("ejabberd_http.hrl").
 +
 +-define(CT_PLAIN,
 +        {<<"Content-Type">>, <<"text/plain">>}).
 +
 +-define(CT_XML,
 +        {<<"Content-Type">>, <<"text/xml; charset=utf-8">>}).
 +
 +-define(CT_JSON,
 +        {<<"Content-Type">>, <<"application/json">>}).
 +
 +-define(AC_ALLOW_ORIGIN,
 +        {<<"Access-Control-Allow-Origin">>, <<"*">>}).
 +
 +-define(AC_ALLOW_METHODS,
 +        {<<"Access-Control-Allow-Methods">>,
 +         <<"GET, POST, OPTIONS">>}).
 +
 +-define(AC_ALLOW_HEADERS,
 +        {<<"Access-Control-Allow-Headers">>,
 +         <<"Content-Type">>}).
 +
 +-define(AC_MAX_AGE,
 +        {<<"Access-Control-Max-Age">>, <<"86400">>}).
 +
 +-define(OPTIONS_HEADER,
 +        [?CT_PLAIN, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_METHODS,
 +         ?AC_ALLOW_HEADERS, ?AC_MAX_AGE]).
 +
 +-define(HEADER(CType),
 +        [CType, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_HEADERS]).
 +
 +%% -------------------
 +%% Module control
 +%% -------------------
 +
 +start(_Host, _Opts) ->
 +    ok.
 +
 +stop(_Host) ->
 +    ok.
 +
 +
 +%% ----------
 +%% basic auth
 +%% ----------
 +
 +check_permissions(#request{auth = HTTPAuth, headers = Headers}, Command)
 +  when HTTPAuth /= undefined ->
 +    case catch binary_to_existing_atom(Command, utf8) of
 +        Call when is_atom(Call) ->
 +            Admin =
 +                case lists:keysearch(<<"X-Admin">>, 1, Headers) of
 +                    {value, {_, <<"true">>}} -> true;
 +                    _ -> false
 +                end,
 +            Auth =
 +                case HTTPAuth of
 +                    {SJID, Pass} ->
 +                        case jid:from_string(SJID) of
 +                            #jid{user = User, server = Server} ->
++                                case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of
 +                                    true -> {ok, {User, Server, Pass, Admin}};
 +                                    false -> false
 +                                end;
 +                            _ ->
 +                                false
 +                        end;
 +                    {oauth, Token, _} ->
 +                        case ejabberd_oauth:check_token(Command, Token) of
 +                            {ok, User, Server} ->
 +                                {ok, {User, Server, {oauth, Token}, Admin}};
 +                            false ->
 +                                false
 +                        end;
 +                    _ ->
 +                        false
 +                end,
 +            case Auth of
 +                {ok, A} -> {allowed, Call, A};
 +                _ -> unauthorized_response()
 +            end;
 +        _ ->
 +            unauthorized_response()
 +    end;
 +check_permissions(_, _Command) ->
 +    unauthorized_response().
 +
 +%% ------------------
 +%% command processing
 +%% ------------------
 +
 +process(_, #request{method = 'POST', data = <<>>}) ->
 +    ?DEBUG("Bad Request: no data", []),
 +    badrequest_response();
 +process([Call], #request{method = 'POST', data = Data, ip = IP} = Req) ->
 +    try
 +        Args = case jiffy:decode(Data) of
 +            List when is_list(List) -> List;
 +            {List} when is_list(List) -> List;
 +            Other -> [Other]
 +        end,
 +        log(Call, Args, IP),
 +        case check_permissions(Req, Call) of
 +            {allowed, Cmd, Auth} ->
 +                {Code, Result} = handle(Cmd, Auth, Args),
 +                json_response(Code, jiffy:encode(Result));
 +            ErrorResponse ->
 +                ErrorResponse
 +        end
 +    catch _:Error ->
 +        ?DEBUG("Bad Request: ~p", [Error]),
 +        badrequest_response()
 +    end;
 +process([Call], #request{method = 'GET', q = Data, ip = IP} = Req) ->
 +    try
 +        Args = case Data of
 +            [{nokey, <<>>}] -> [];
 +            _ -> Data
 +        end,
 +        log(Call, Args, IP),
 +        case check_permissions(Req, Call) of
 +            {allowed, Cmd, Auth} ->
 +                {Code, Result} = handle(Cmd, Auth, Args),
 +                json_response(Code, jiffy:encode(Result));
 +            ErrorResponse ->
 +                ErrorResponse
 +        end
 +    catch _:Error ->
 +        ?DEBUG("Bad Request: ~p", [Error]),
 +        badrequest_response()
 +    end;
 +process([], #request{method = 'OPTIONS', data = <<>>}) ->
 +    {200, ?OPTIONS_HEADER, []};
 +process(_Path, Request) ->
 +    ?DEBUG("Bad Request: no handler ~p", [Request]),
 +    badrequest_response().
 +
 +%% ----------------
 +%% command handlers
 +%% ----------------
 +
 +% generic ejabberd command handler
 +handle(Call, Auth, Args) when is_atom(Call), is_list(Args) ->
 +    case ejabberd_commands:get_command_format(Call, Auth) of
 +        {ArgsSpec, _} when is_list(ArgsSpec) ->
 +            Args2 = [{jlib:binary_to_atom(Key), Value} || {Key, Value} <- Args],
 +            Spec = lists:foldr(
 +                    fun ({Key, binary}, Acc) ->
 +                            [{Key, <<>>}|Acc];
 +                        ({Key, string}, Acc) ->
 +                            [{Key, <<>>}|Acc];
 +                        ({Key, integer}, Acc) ->
 +                            [{Key, 0}|Acc];
 +                        ({Key, {list, _}}, Acc) ->
 +                            [{Key, []}|Acc];
 +                        ({Key, atom}, Acc) ->
 +                            [{Key, undefined}|Acc]
 +                    end, [], ArgsSpec),
 +            handle2(Call, Auth, match(Args2, Spec));
 +        {error, Msg} ->
 +            {400, Msg};
 +        _Error ->
 +            {400, <<"Error">>}
 +    end.
 +
 +handle2(Call, Auth, Args) when is_atom(Call), is_list(Args) ->
 +    {ArgsF, _ResultF} = ejabberd_commands:get_command_format(Call, Auth),
 +    ArgsFormatted = format_args(Args, ArgsF),
 +    case ejabberd_command(Auth, Call, ArgsFormatted, 400) of
 +        0 -> {200, <<"OK">>};
 +        1 -> {500, <<"500 Internal server error">>};
 +        400 -> {400, <<"400 Bad Request">>};
 +        404 -> {404, <<"404 Not found">>};
 +        Res -> format_command_result(Call, Auth, Res)
 +    end.
 +
 +get_elem_delete(A, L) ->
 +    case proplists:get_all_values(A, L) of
 +      [Value] -> {Value, proplists:delete(A, L)};
 +      [_, _ | _] ->
 +        %% Crash reporting the error
 +        exit({duplicated_attribute, A, L});
 +      [] ->
 +        %% Report the error and then force a crash
 +        exit({attribute_not_found, A, L})
 +    end.
 +
 +format_args(Args, ArgsFormat) ->
 +    {ArgsRemaining, R} = lists:foldl(fun ({ArgName,
 +                                         ArgFormat},
 +                                        {Args1, Res}) ->
 +                                           {ArgValue, Args2} =
 +                                               get_elem_delete(ArgName,
 +                                                               Args1),
 +                                           Formatted = format_arg(ArgValue,
 +                                                                  ArgFormat),
 +                                           {Args2, Res ++ [Formatted]}
 +                                   end,
 +                                   {Args, []}, ArgsFormat),
 +    case ArgsRemaining of
 +      [] -> R;
 +      L when is_list(L) -> exit({additional_unused_args, L})
 +    end.
 +
 +format_arg({array, Elements},
 +         {list, {ElementDefName, ElementDefFormat}})
 +    when is_list(Elements) ->
 +    lists:map(fun ({struct, [{ElementName, ElementValue}]}) when
 +                        ElementDefName == ElementName ->
 +                    format_arg(ElementValue, ElementDefFormat)
 +            end,
 +            Elements);
 +format_arg({array, [{struct, Elements}]},
 +         {list, {ElementDefName, ElementDefFormat}})
 +    when is_list(Elements) ->
 +    lists:map(fun ({ElementName, ElementValue}) ->
 +                    true = ElementDefName == ElementName,
 +                    format_arg(ElementValue, ElementDefFormat)
 +            end,
 +            Elements);
 +format_arg({array, [{struct, Elements}]},
 +         {tuple, ElementsDef})
 +    when is_list(Elements) ->
 +    FormattedList = format_args(Elements, ElementsDef),
 +    list_to_tuple(FormattedList);
 +format_arg({array, Elements}, {list, ElementsDef})
 +    when is_list(Elements) and is_atom(ElementsDef) ->
 +    [format_arg(Element, ElementsDef)
 +     || Element <- Elements];
 +format_arg(Arg, integer) when is_integer(Arg) -> Arg;
 +format_arg(Arg, binary) when is_list(Arg) -> process_unicode_codepoints(Arg);
 +format_arg(Arg, binary) when is_binary(Arg) -> Arg;
 +format_arg(Arg, string) when is_list(Arg) -> process_unicode_codepoints(Arg);
 +format_arg(Arg, string) when is_binary(Arg) -> Arg;
 +format_arg(undefined, binary) -> <<>>;
 +format_arg(undefined, string) -> <<>>;
 +format_arg(Arg, Format) ->
 +    ?ERROR_MSG("don't know how to format Arg ~p for format ~p", [Arg, Format]),
 +    error.
 +
 +process_unicode_codepoints(Str) ->
 +    iolist_to_binary(lists:map(fun(X) when X > 255 -> unicode:characters_to_binary([X]);
 +                                  (Y) -> Y
 +                               end, Str)).
 +
 +%% ----------------
 +%% internal helpers
 +%% ----------------
 +
 +match(Args, Spec) ->
 +    [{Key, proplists:get_value(Key, Args, Default)} || {Key, Default} <- Spec].
 +
 +ejabberd_command(Auth, Cmd, Args, Default) ->
 +    case catch ejabberd_commands:execute_command(undefined, Auth, Cmd, Args) of
 +        {'EXIT', _} -> Default;
 +        {error, _} -> Default;
 +        Result -> Result
 +    end.
 +
 +format_command_result(Cmd, Auth, Result) ->
 +    {_, ResultFormat} = ejabberd_commands:get_command_format(Cmd, Auth),
 +    case {ResultFormat, Result} of
 +        {{_, rescode}, V} when V == true; V == ok ->
 +            {200, <<"">>};
 +        {{_, rescode}, _} ->
 +            {500, <<"">>};
 +        {{_, restuple}, {V1, Text1}} when V1 == true; V1 == ok ->
 +            {200, iolist_to_binary(Text1)};
 +        {{_, restuple}, {_, Text2}} ->
 +            {500, iolist_to_binary(Text2)};
 +        {{_, {list, _}}, _V} ->
 +            {_, L} = format_result(Result, ResultFormat),
 +            {200, L};
 +        {{_, {tuple, _}}, _V} ->
 +            {_, T} = format_result(Result, ResultFormat),
 +            {200, T};
 +        _ ->
 +            {200, {[format_result(Result, ResultFormat)]}}
 +    end.
 +
 +format_result(Atom, {Name, atom}) ->
 +    {jlib:atom_to_binary(Name), jlib:atom_to_binary(Atom)};
 +
 +format_result(Int, {Name, integer}) ->
 +    {jlib:atom_to_binary(Name), Int};
 +
 +format_result(String, {Name, string}) ->
 +    {jlib:atom_to_binary(Name), iolist_to_binary(String)};
 +
 +format_result(Code, {Name, rescode}) ->
 +    {jlib:atom_to_binary(Name), Code == true orelse Code == ok};
 +
 +format_result({Code, Text}, {Name, restuple}) ->
 +    {jlib:atom_to_binary(Name),
 +     {[{<<"res">>, Code == true orelse Code == ok},
 +       {<<"text">>, iolist_to_binary(Text)}]}};
 +
 +format_result(Els, {Name, {list, {_, {tuple, [{_, atom}, _]}} = Fmt}}) ->
 +    {jlib:atom_to_binary(Name), {[format_result(El, Fmt) || El <- Els]}};
 +
 +format_result(Els, {Name, {list, Def}}) ->
 +    {jlib:atom_to_binary(Name), [element(2, format_result(El, Def)) || El <- Els]};
 +
 +format_result(Tuple, {_Name, {tuple, [{_, atom}, ValFmt]}}) ->
 +    {Name2, Val} = Tuple,
 +    {_, Val2} = format_result(Val, ValFmt),
 +    {jlib:atom_to_binary(Name2), Val2};
 +
 +format_result(Tuple, {Name, {tuple, Def}}) ->
 +    Els = lists:zip(tuple_to_list(Tuple), Def),
 +    {jlib:atom_to_binary(Name), {[format_result(El, ElDef) || {El, ElDef} <- Els]}};
 +
 +format_result(404, {_Name, _}) ->
 +    "not_found".
 +
 +unauthorized_response() ->
 +    {401, ?HEADER(?CT_XML),
 +     #xmlel{name = <<"h1">>, attrs = [],
 +            children = [{xmlcdata, <<"401 Unauthorized">>}]}}.
 +
 +badrequest_response() ->
 +    {400, ?HEADER(?CT_XML),
 +     #xmlel{name = <<"h1">>, attrs = [],
 +            children = [{xmlcdata, <<"400 Bad Request">>}]}}.
 +json_response(Code, Body) when is_integer(Code) ->
 +    {Code, ?HEADER(?CT_JSON), Body}.
 +
 +log(Call, Args, {Addr, Port}) ->
 +    AddrS = jlib:ip_to_list({Addr, Port}),
 +    ?INFO_MSG("Admin call ~s ~p from ~s:~p", [Call, Args, AddrS, Port]).
 +
 +mod_opt_type(access) ->
 +    fun(Access) when is_atom(Access) -> Access end;
 +mod_opt_type(_) -> [access].
Simple merge
Simple merge