diff options
author | Christophe Romain <christophe.romain@process-one.net> | 2015-09-25 14:53:25 +0200 |
---|---|---|
committer | Christophe Romain <christophe.romain@process-one.net> | 2015-09-25 15:49:07 +0200 |
commit | a1129dc96b4782800c3046ea6de6a77049f2293e (patch) | |
tree | 16d24c6eefbe7a5f65c0ac4410d8a57eed1d1222 /src/ejabberd_commands.erl | |
parent | Merge pull request #742 from joudinet/master (diff) |
Add OAuth support (thanks to Aleksey)
Diffstat (limited to 'src/ejabberd_commands.erl')
-rw-r--r-- | src/ejabberd_commands.erl | 185 |
1 files changed, 167 insertions, 18 deletions
diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index a5ba98ae..34297e00 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -211,12 +211,15 @@ -export([init/0, list_commands/0, get_command_format/1, + get_command_format/2, get_command_definition/1, get_tags_commands/0, + get_commands/0, register_commands/1, unregister_commands/1, execute_command/2, - execute_command/4 + execute_command/4, + opt_type/1 ]). -include("ejabberd_commands.hrl"). @@ -265,19 +268,39 @@ list_commands() -> _ = '_'}), [{A, B, C} || [A, B, C] <- Commands]. +-spec list_commands_policy() -> [{atom(), [aterm()], string(), atom()}]. + +%% @doc Get a list of all the available commands, arguments, description, and +%% policy. +list_commands_policy() -> + Commands = ets:match(ejabberd_commands, + #ejabberd_commands{name = '$1', + args = '$2', + desc = '$3', + policy = '$4', + _ = '_'}), + [{A, B, C, D} || [A, B, C, D] <- Commands]. + -spec get_command_format(atom()) -> {[aterm()], rterm()} | {error, command_unknown}. %% @doc Get the format of arguments and result of a command. get_command_format(Name) -> + get_command_format(Name, noauth). + +get_command_format(Name, Auth) -> + Admin = is_admin(Name, Auth), Matched = ets:match(ejabberd_commands, #ejabberd_commands{name = Name, args = '$1', result = '$2', + policy = '$3', _ = '_'}), case Matched of [] -> {error, command_unknown}; - [[Args, Result]] -> + [[Args, Result, user]] when Admin -> + {[{user, binary}, {server, binary} | Args], Result}; + [[Args, Result, _]] -> {Args, Result} end. @@ -295,24 +318,54 @@ get_command_definition(Name) -> execute_command(Name, Arguments) -> execute_command([], noauth, Name, Arguments). +-spec execute_command([{atom(), [atom()], [any()]}], + {binary(), binary(), binary(), boolean()} | + noauth | admin, + atom(), + [any()] + ) -> any(). + %% @spec (AccessCommands, Auth, Name::atom(), Arguments) -> ResultTerm | {error, Error} %% where %% AccessCommands = [{Access, CommandNames, Arguments}] -%% Auth = {User::string(), Server::string(), Password::string()} | noauth +%% Auth = {User::string(), Server::string(), Password::string(), Admin::boolean()} +%% | noauth +%% | admin %% Method = atom() %% Arguments = [any()] %% Error = command_unknown | account_unprivileged | invalid_account_data | no_auth_provided -execute_command(AccessCommands, Auth, Name, Arguments) -> +execute_command(AccessCommands1, Auth1, Name, Arguments) -> + Auth = case is_admin(Name, Auth1) of + true -> admin; + false -> Auth1 + end, case ets:lookup(ejabberd_commands, Name) of [Command] -> + AccessCommands = get_access_commands(AccessCommands1), try check_access_commands(AccessCommands, Auth, Name, Command, Arguments) of - ok -> execute_command2(Command, Arguments) + ok -> execute_command2(Auth, Command, Arguments) catch {error, Error} -> {error, Error} end; [] -> {error, command_unknown} end. +execute_command2( + _Auth, #ejabberd_commands{policy = open} = Command, Arguments) -> + execute_command2(Command, Arguments); +execute_command2( + _Auth, #ejabberd_commands{policy = restricted} = Command, Arguments) -> + execute_command2(Command, Arguments); +execute_command2( + _Auth, #ejabberd_commands{policy = admin} = Command, Arguments) -> + execute_command2(Command, Arguments); +execute_command2( + admin, #ejabberd_commands{policy = user} = Command, Arguments) -> + execute_command2(Command, Arguments); +execute_command2( + {User, Server, _, _}, #ejabberd_commands{policy = user} = Command, Arguments) -> + execute_command2(Command, [User, Server | Arguments]). + execute_command2(Command, Arguments) -> Module = Command#ejabberd_commands.module, Function = Command#ejabberd_commands.function, @@ -372,11 +425,20 @@ get_tags_commands() -> %% Error = account_unprivileged | invalid_account_data check_access_commands([], _Auth, _Method, _Command, _Arguments) -> ok; -check_access_commands(AccessCommands, Auth, Method, Command, Arguments) -> +check_access_commands(AccessCommands, Auth, Method, Command1, Arguments) -> + Command = + case {Command1#ejabberd_commands.policy, Auth} of + {user, admin} -> + Command1#ejabberd_commands{ + args = [{user, binary}, {server, binary} | + Command1#ejabberd_commands.args]}; + _ -> + Command1 + end, AccessCommandsAllowed = lists:filter( fun({Access, Commands, ArgumentRestrictions}) -> - case check_access(Access, Auth) of + case check_access(Command, Access, Auth) of true -> check_access_command(Commands, Command, ArgumentRestrictions, Method, Arguments); @@ -385,7 +447,7 @@ check_access_commands(AccessCommands, Auth, Method, Command, Arguments) -> end; ({Access, Commands}) -> ArgumentRestrictions = [], - case check_access(Access, Auth) of + case check_access(Command, Access, Auth) of true -> check_access_command(Commands, Command, ArgumentRestrictions, Method, Arguments); @@ -399,29 +461,48 @@ check_access_commands(AccessCommands, Auth, Method, Command, Arguments) -> 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 true -> {ok, User, Server}; _ -> throw({error, invalid_account_data}) end. -check_access(all, _) -> +check_access(Command, all, _) + when Command#ejabberd_commands.policy == open -> + true; +check_access(_Command, _Access, admin) -> true; -check_access(Access, Auth) -> - case check_auth(Auth) of +check_access(_Command, _Access, {_User, _Server, _, true}) -> + false; +check_access(Command, Access, Auth) + when 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(Access, User, Server) -> %% Check this user has access permission case acl:match_rule(Server, Access, jlib:make_jid(User, Server, <<"">>)) of allow -> true; @@ -452,3 +533,71 @@ tag_arguments(ArgsDefs, Args) -> end, ArgsDefs, Args). + + +get_access_commands(undefined) -> + Cmds = get_commands(), + [{all, Cmds, []}]; +get_access_commands(AccessCommands) -> + AccessCommands. + +get_commands() -> + Opts = ejabberd_config:get_option( + commands, + fun(V) when is_list(V) -> V end, + []), + CommandsList = list_commands_policy(), + OpenCmds = [N || {N, _, _, open} <- CommandsList], + RestrictedCmds = [N || {N, _, _, restricted} <- CommandsList], + AdminCmds = [N || {N, _, _, admin} <- CommandsList], + UserCmds = [N || {N, _, _, user} <- CommandsList], + Cmds = + lists:foldl( + fun({add_commands, L}, Acc) -> + Cmds = case L of + open -> OpenCmds; + restricted -> RestrictedCmds; + admin -> AdminCmds; + user -> UserCmds; + _ when is_list(L) -> L + end, + lists:usort(Cmds ++ Acc); + ({remove_commands, L}, Acc) -> + Cmds = case L of + open -> OpenCmds; + restricted -> RestrictedCmds; + admin -> AdminCmds; + user -> UserCmds; + _ when is_list(L) -> L + end, + Acc -- Cmds; + (_, Acc) -> Acc + end, AdminCmds ++ UserCmds, Opts), + Cmds. + +is_admin(_Name, noauth) -> + false; +is_admin(_Name, admin) -> + true; +is_admin(_Name, {_User, _Server, _, false}) -> + false; +is_admin(Name, {User, Server, _, true} = Auth) -> + AdminAccess = ejabberd_config:get_option( + commands_admin_access, + fun(A) when is_atom(A) -> A end, + none), + case acl:match_rule(Server, AdminAccess, + jlib:make_jid(User, Server, <<"">>)) of + allow -> + case catch check_auth(get_command_definition(Name), Auth) of + {ok, _, _} -> true; + _ -> false + end; + deny -> false + end. + +opt_type(commands_admin_access) -> + fun(A) when is_atom(A) -> A end; +opt_type(commands) -> + fun(V) when is_list(V) -> V end; +opt_type(_) -> [commands, commands_admin_access]. |