summaryrefslogtreecommitdiff
path: root/src/ejabberd_commands.erl
diff options
context:
space:
mode:
authorChristophe Romain <christophe.romain@process-one.net>2015-09-25 14:53:25 +0200
committerChristophe Romain <christophe.romain@process-one.net>2015-09-25 15:49:07 +0200
commita1129dc96b4782800c3046ea6de6a77049f2293e (patch)
tree16d24c6eefbe7a5f65c0ac4410d8a57eed1d1222 /src/ejabberd_commands.erl
parentMerge 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.erl185
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].