aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Shchepin <alexey@process-one.net>2019-09-18 18:45:51 +0300
committerAlexey Shchepin <alexey@process-one.net>2019-09-18 18:46:24 +0300
commit0fe1e40a9d6edb6f70d76f60b8f3dc95ae6a34eb (patch)
treef576cb4f0cc88b5edfaee69565d28d92c9802db3
parentmod_jidprep: Don't call gen_mod functions directly (diff)
JWT-only authentication for some users (#3012)
-rw-r--r--src/ejabberd_auth.erl38
-rw-r--r--src/ejabberd_auth_jwt.erl17
-rw-r--r--src/ejabberd_option.erl8
-rw-r--r--src/ejabberd_options.erl7
4 files changed, 52 insertions, 18 deletions
diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl
index 2f4983500..9a7479e40 100644
--- a/src/ejabberd_auth.erl
+++ b/src/ejabberd_auth.erl
@@ -76,7 +76,7 @@
{ets_cache:tag(), {ok, password()} | {error, db_failure | not_allowed}}.
-callback remove_user(binary(), binary()) -> ok | {error, db_failure | not_allowed}.
-callback user_exists(binary(), binary()) -> {ets_cache:tag(), boolean() | {error, db_failure}}.
--callback check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean()}.
+-callback check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean() | {stop, boolean()}}.
-callback try_register(binary(), binary(), password()) ->
{ets_cache:tag(), {ok, password()} | {error, exists | db_failure | not_allowed}}.
-callback get_users(binary(), opts()) -> [{binary(), binary()}].
@@ -237,17 +237,20 @@ check_password_with_authmodule(User, AuthzId, Server, Password, Digest, DigestGe
error ->
false;
LAuthzId ->
- lists:foldl(
- fun(Mod, false) ->
- case db_check_password(
- LUser, LAuthzId, LServer, Password,
- Digest, DigestGen, Mod) of
- true -> {true, Mod};
- false -> false
- end;
- (_, Acc) ->
- Acc
- end, false, auth_modules(LServer))
+ untag_stop(
+ lists:foldl(
+ fun(Mod, false) ->
+ case db_check_password(
+ LUser, LAuthzId, LServer, Password,
+ Digest, DigestGen, Mod) of
+ true -> {true, Mod};
+ false -> false;
+ {stop, true} -> {stop, {true, Mod}};
+ {stop, false} -> {stop, false}
+ end;
+ (_, Acc) ->
+ Acc
+ end, false, auth_modules(LServer)))
end;
_ ->
false
@@ -484,7 +487,11 @@ remove_user(User, Server, Password) ->
<<"">>, undefined, Mod) of
true ->
db_remove_user(LUser, LServer, Mod);
+ {stop, true} ->
+ db_remove_user(LUser, LServer, Mod);
false ->
+ {error, not_allowed};
+ {stop, false} ->
{error, not_allowed}
end
end, {error, not_allowed}, auth_modules(Server)) of
@@ -654,7 +661,9 @@ db_check_password(User, AuthzId, Server, ProvidedPassword,
case Mod:check_password(
User, AuthzId, Server, ProvidedPassword) of
{CacheTag, true} -> {CacheTag, {ok, ProvidedPassword}};
- {CacheTag, false} -> {CacheTag, error}
+ {CacheTag, {stop, true}} -> {CacheTag, {ok, ProvidedPassword}};
+ {CacheTag, false} -> {CacheTag, error};
+ {CacheTag, {stop, false}} -> {CacheTag, error}
end
end) of
{ok, _} ->
@@ -891,6 +900,9 @@ validate_credentials(User, Server, Password) ->
end
end.
+untag_stop({stop, Val}) -> Val;
+untag_stop(Val) -> Val.
+
import_info() ->
[{<<"users">>, 3}].
diff --git a/src/ejabberd_auth_jwt.erl b/src/ejabberd_auth_jwt.erl
index 3b3698d1c..33c6cc601 100644
--- a/src/ejabberd_auth_jwt.erl
+++ b/src/ejabberd_auth_jwt.erl
@@ -31,7 +31,7 @@
-export([start/1, stop/1, check_password/4,
store_type/1, plain_password_required/1,
- user_exists/2
+ user_exists/2, use_cache/1
]).
-include("xmpp.hrl").
@@ -55,7 +55,7 @@ plain_password_required(_Host) -> true.
store_type(_Host) -> external.
--spec check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean()}.
+-spec check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean() | {stop, boolean()}}.
check_password(User, AuthzId, Server, Token) ->
%% MREMOND: Should we move the AuthzId check at a higher level in
%% the call stack?
@@ -64,12 +64,23 @@ check_password(User, AuthzId, Server, Token) ->
true ->
if Token == <<"">> -> {nocache, false};
true ->
- {nocache, check_jwt_token(User, Server, Token)}
+ Res = check_jwt_token(User, Server, Token),
+ Rule = ejabberd_option:jwt_auth_only_rule(Server),
+ case acl:match_rule(Server, Rule,
+ jid:make(User, Server, <<"">>)) of
+ deny ->
+ {nocache, Res};
+ allow ->
+ {nocache, {stop, Res}}
+ end
end
end.
user_exists(_User, _Host) -> {nocache, false}.
+use_cache(_) ->
+ false.
+
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl
index d7fd65cfe..9e6b14043 100644
--- a/src/ejabberd_option.erl
+++ b/src/ejabberd_option.erl
@@ -50,6 +50,7 @@
-export([host_config/0]).
-export([hosts/0]).
-export([include_config_file/0, include_config_file/1]).
+-export([jwt_auth_only_rule/0, jwt_auth_only_rule/1]).
-export([jwt_key/0, jwt_key/1]).
-export([language/0, language/1]).
-export([ldap_backups/0, ldap_backups/1]).
@@ -424,6 +425,13 @@ include_config_file() ->
include_config_file(Host) ->
ejabberd_config:get_option({include_config_file, Host}).
+-spec jwt_auth_only_rule() -> atom().
+jwt_auth_only_rule() ->
+ jwt_auth_only_rule(global).
+-spec jwt_auth_only_rule(global | binary()) -> atom().
+jwt_auth_only_rule(Host) ->
+ ejabberd_config:get_option({jwt_auth_only_rule, Host}).
+
-spec jwt_key() -> jose_jwk:key() | 'undefined'.
jwt_key() ->
jwt_key(global).
diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl
index a83f0add7..7764f451b 100644
--- a/src/ejabberd_options.erl
+++ b/src/ejabberd_options.erl
@@ -409,7 +409,9 @@ opt_type(jwt_key) ->
{error, Reason} ->
econf:fail({read_file, Reason, Path})
end
- end).
+ end);
+opt_type(jwt_auth_only_rule) ->
+ econf:atom().
%% We only define the types of options that cannot be derived
%% automatically by tools/opt_type.sh script
@@ -635,7 +637,8 @@ options() ->
{websocket_origin, []},
{websocket_ping_interval, timer:seconds(60)},
{websocket_timeout, timer:minutes(5)},
- {jwt_key, undefined}].
+ {jwt_key, undefined},
+ {jwt_auth_only_rule, none}].
-spec globals() -> [atom()].
globals() ->