Merge pull request #8108 from savonarola/authn-delete-chain

Authn delete chain
This commit is contained in:
Ilya Averyanov 2022-06-03 12:16:42 +03:00 committed by GitHub
commit 4adfe75530
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 226 additions and 198 deletions

View File

@ -55,6 +55,9 @@ jobs:
arch: arch:
- amd64 - amd64
steps: steps:
- uses: erlef/setup-beam@v1
with:
otp-version: "24.2"
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v2
with: with:
name: source name: source
@ -135,13 +138,15 @@ jobs:
# - emqx-enterprise # TODO test enterprise # - emqx-enterprise # TODO test enterprise
steps: steps:
- uses: erlef/setup-beam@v1
with:
otp-version: "24.2"
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v2
with: with:
name: source name: source
path: . path: .
- name: unzip source code - name: unzip source code
run: unzip -q source.zip run: unzip -q source.zip
- name: Get deps git refs for cache - name: Get deps git refs for cache
id: deps-refs id: deps-refs
run: | run: |

View File

@ -14,6 +14,9 @@ jobs:
outputs: outputs:
version: ${{ steps.build_docker.outputs.version}} version: ${{ steps.build_docker.outputs.version}}
steps: steps:
- uses: erlef/setup-beam@v1
with:
otp-version: "24.2"
- name: download jmeter - name: download jmeter
timeout-minutes: 3 timeout-minutes: 3
env: env:
@ -54,6 +57,9 @@ jobs:
needs: build_emqx_for_jmeter_tests needs: build_emqx_for_jmeter_tests
steps: steps:
- uses: erlef/setup-beam@v1
with:
otp-version: "24.2"
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v2
with: with:
@ -145,6 +151,9 @@ jobs:
needs: build_emqx_for_jmeter_tests needs: build_emqx_for_jmeter_tests
steps: steps:
- uses: erlef/setup-beam@v1
with:
otp-version: "24.2"
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v2
with: with:
@ -246,6 +255,9 @@ jobs:
needs: build_emqx_for_jmeter_tests needs: build_emqx_for_jmeter_tests
steps: steps:
- uses: erlef/setup-beam@v1
with:
otp-version: "24.2"
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v2
with: with:
@ -343,6 +355,9 @@ jobs:
needs: build_emqx_for_jmeter_tests needs: build_emqx_for_jmeter_tests
steps: steps:
- uses: erlef/setup-beam@v1
with:
otp-version: "24.2"
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v2
with: with:
@ -437,6 +452,9 @@ jobs:
needs: build_emqx_for_jmeter_tests needs: build_emqx_for_jmeter_tests
steps: steps:
- uses: erlef/setup-beam@v1
with:
otp-version: "24.2"
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v2
with: with:

View File

@ -47,7 +47,6 @@
register_providers/1, register_providers/1,
deregister_provider/1, deregister_provider/1,
deregister_providers/1, deregister_providers/1,
create_chain/1,
delete_chain/1, delete_chain/1,
lookup_chain/1, lookup_chain/1,
list_chains/0, list_chains/0,
@ -81,7 +80,7 @@
]). ]).
%% utility functions %% utility functions
-export([authenticator_id/1]). -export([authenticator_id/1, metrics_id/2]).
%% proxy callback %% proxy callback
-export([ -export([
@ -216,22 +215,28 @@ when
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
authenticate(#{listener := Listener, protocol := Protocol} = Credential, _AuthResult) -> authenticate(#{listener := Listener, protocol := Protocol} = Credential, _AuthResult) ->
Authenticators = get_authenticators(Listener, global_chain(Protocol)), case get_authenticators(Listener, global_chain(Protocol)) of
{ok, ChainName, Authenticators} ->
case get_enabled(Authenticators) of case get_enabled(Authenticators) of
[] -> ignore; [] ->
NAuthenticators -> do_authenticate(NAuthenticators, Credential) ignore;
NAuthenticators ->
do_authenticate(ChainName, NAuthenticators, Credential)
end;
none ->
ignore
end. end.
get_authenticators(Listener, Global) -> get_authenticators(Listener, Global) ->
case ets:lookup(?CHAINS_TAB, Listener) of case ets:lookup(?CHAINS_TAB, Listener) of
[#chain{authenticators = Authenticators}] -> [#chain{name = Name, authenticators = Authenticators}] ->
Authenticators; {ok, Name, Authenticators};
_ -> _ ->
case ets:lookup(?CHAINS_TAB, Global) of case ets:lookup(?CHAINS_TAB, Global) of
[#chain{authenticators = Authenticators}] -> [#chain{name = Name, authenticators = Authenticators}] ->
Authenticators; {ok, Name, Authenticators};
_ -> _ ->
[] none
end end
end. end.
@ -265,7 +270,6 @@ authenticator_id(Config) ->
initialize_authentication(_, []) -> initialize_authentication(_, []) ->
ok; ok;
initialize_authentication(ChainName, AuthenticatorsConfig) -> initialize_authentication(ChainName, AuthenticatorsConfig) ->
_ = create_chain(ChainName),
CheckedConfig = to_list(AuthenticatorsConfig), CheckedConfig = to_list(AuthenticatorsConfig),
lists:foreach( lists:foreach(
fun(AuthenticatorConfig) -> fun(AuthenticatorConfig) ->
@ -316,10 +320,6 @@ deregister_providers(AuthNTypes) when is_list(AuthNTypes) ->
deregister_provider(AuthNType) -> deregister_provider(AuthNType) ->
deregister_providers([AuthNType]). deregister_providers([AuthNType]).
-spec create_chain(chain_name()) -> {ok, chain()} | {error, term()}.
create_chain(Name) ->
call({create_chain, Name}).
-spec delete_chain(chain_name()) -> ok | {error, term()}. -spec delete_chain(chain_name()) -> ok | {error, term()}.
delete_chain(Name) -> delete_chain(Name) ->
call({delete_chain, Name}). call({delete_chain, Name}).
@ -445,50 +445,36 @@ handle_call(
end; end;
handle_call({deregister_providers, AuthNTypes}, _From, #{providers := Providers} = State) -> handle_call({deregister_providers, AuthNTypes}, _From, #{providers := Providers} = State) ->
reply(ok, State#{providers := maps:without(AuthNTypes, Providers)}); reply(ok, State#{providers := maps:without(AuthNTypes, Providers)});
handle_call({create_chain, Name}, _From, State) -> handle_call({delete_chain, ChainName}, _From, State) ->
case ets:member(?CHAINS_TAB, Name) of UpdateFun = fun(Chain) ->
true -> {_MatchedIDs, NewChain} = do_delete_authenticators(fun(_) -> true end, Chain),
reply({error, {already_exists, {chain, Name}}}, State); {ok, ok, NewChain}
false -> end,
Chain = #chain{ Reply = with_chain(ChainName, UpdateFun),
name = Name, reply(Reply, maybe_unhook(State));
authenticators = []
},
true = ets:insert(?CHAINS_TAB, Chain),
reply({ok, serialize_chain(Chain)}, State)
end;
handle_call({delete_chain, Name}, _From, State) ->
case ets:lookup(?CHAINS_TAB, Name) of
[] ->
reply({error, {not_found, {chain, Name}}}, State);
[#chain{authenticators = Authenticators}] ->
_ = [do_destroy_authenticator(Authenticator) || Authenticator <- Authenticators],
true = ets:delete(?CHAINS_TAB, Name),
reply(ok, maybe_unhook(State))
end;
handle_call({create_authenticator, ChainName, Config}, _From, #{providers := Providers} = State) -> handle_call({create_authenticator, ChainName, Config}, _From, #{providers := Providers} = State) ->
UpdateFun = fun(Chain) -> UpdateFun = fun(Chain) ->
handle_create_authenticator(Chain, Config, Providers) handle_create_authenticator(Chain, Config, Providers)
end, end,
Reply = update_chain(ChainName, UpdateFun), Reply = with_new_chain(ChainName, UpdateFun),
reply(Reply, maybe_hook(State)); reply(Reply, maybe_hook(State));
handle_call({delete_authenticator, ChainName, AuthenticatorID}, _From, State) -> handle_call({delete_authenticator, ChainName, AuthenticatorID}, _From, State) ->
UpdateFun = fun(Chain) -> UpdateFun = fun(Chain) ->
handle_delete_authenticator(Chain, AuthenticatorID) handle_delete_authenticator(Chain, AuthenticatorID)
end, end,
Reply = update_chain(ChainName, UpdateFun), Reply = with_chain(ChainName, UpdateFun),
reply(Reply, maybe_unhook(State)); reply(Reply, maybe_unhook(State));
handle_call({update_authenticator, ChainName, AuthenticatorID, Config}, _From, State) -> handle_call({update_authenticator, ChainName, AuthenticatorID, Config}, _From, State) ->
UpdateFun = fun(Chain) -> UpdateFun = fun(Chain) ->
handle_update_authenticator(Chain, AuthenticatorID, Config) handle_update_authenticator(Chain, AuthenticatorID, Config)
end, end,
Reply = update_chain(ChainName, UpdateFun), Reply = with_chain(ChainName, UpdateFun),
reply(Reply, State); reply(Reply, State);
handle_call({move_authenticator, ChainName, AuthenticatorID, Position}, _From, State) -> handle_call({move_authenticator, ChainName, AuthenticatorID, Position}, _From, State) ->
UpdateFun = fun(Chain) -> UpdateFun = fun(Chain) ->
handle_move_authenticator(Chain, AuthenticatorID, Position) handle_move_authenticator(Chain, AuthenticatorID, Position)
end, end,
Reply = update_chain(ChainName, UpdateFun), Reply = with_chain(ChainName, UpdateFun),
reply(Reply, State); reply(Reply, State);
handle_call({import_users, ChainName, AuthenticatorID, Filename}, _From, State) -> handle_call({import_users, ChainName, AuthenticatorID, Filename}, _From, State) ->
Reply = call_authenticator(ChainName, AuthenticatorID, import_users, [Filename]), Reply = call_authenticator(ChainName, AuthenticatorID, import_users, [Filename]),
@ -563,11 +549,9 @@ handle_update_authenticator(Chain, AuthenticatorID, Config) ->
NewAuthenticator, NewAuthenticator,
Authenticators Authenticators
), ),
true = ets:insert( NewChain = Chain#chain{authenticators = NewAuthenticators},
?CHAINS_TAB, Result = {ok, serialize_authenticator(NewAuthenticator)},
Chain#chain{authenticators = NewAuthenticators} {ok, Result, NewChain};
),
{ok, serialize_authenticator(NewAuthenticator)};
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
end; end;
@ -581,25 +565,24 @@ handle_delete_authenticator(Chain, AuthenticatorID) ->
ID =:= AuthenticatorID ID =:= AuthenticatorID
end, end,
case do_delete_authenticators(MatchFun, Chain) of case do_delete_authenticators(MatchFun, Chain) of
[] -> {[], _NewChain} ->
{error, {not_found, {authenticator, AuthenticatorID}}}; {error, {not_found, {authenticator, AuthenticatorID}}};
[AuthenticatorID] -> {[AuthenticatorID], NewChain} ->
emqx_metrics_worker:clear_metrics(authn_metrics, AuthenticatorID), {ok, ok, NewChain}
ok
end. end.
handle_move_authenticator(Chain, AuthenticatorID, Position) -> handle_move_authenticator(Chain, AuthenticatorID, Position) ->
#chain{authenticators = Authenticators} = Chain, #chain{authenticators = Authenticators} = Chain,
case do_move_authenticator(AuthenticatorID, Authenticators, Position) of case do_move_authenticator(AuthenticatorID, Authenticators, Position) of
{ok, NAuthenticators} -> {ok, NAuthenticators} ->
true = ets:insert(?CHAINS_TAB, Chain#chain{authenticators = NAuthenticators}), NewChain = Chain#chain{authenticators = NAuthenticators},
ok; {ok, ok, NewChain};
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
end. end.
handle_create_authenticator(Chain, Config, Providers) -> handle_create_authenticator(Chain, Config, Providers) ->
#chain{authenticators = Authenticators} = Chain, #chain{name = Name, authenticators = Authenticators} = Chain,
AuthenticatorID = authenticator_id(Config), AuthenticatorID = authenticator_id(Config),
case lists:keymember(AuthenticatorID, #authenticator.id, Authenticators) of case lists:keymember(AuthenticatorID, #authenticator.id, Authenticators) of
true -> true ->
@ -611,31 +594,31 @@ handle_create_authenticator(Chain, Config, Providers) ->
NAuthenticators = NAuthenticators =
Authenticators ++ Authenticators ++
[Authenticator#authenticator{enable = maps:get(enable, Config)}], [Authenticator#authenticator{enable = maps:get(enable, Config)}],
true = ets:insert(
?CHAINS_TAB,
Chain#chain{authenticators = NAuthenticators}
),
ok = emqx_metrics_worker:create_metrics( ok = emqx_metrics_worker:create_metrics(
authn_metrics, authn_metrics,
AuthenticatorID, metrics_id(Name, AuthenticatorID),
[total, success, failed, nomatch], [total, success, failed, nomatch],
[total] [total]
), ),
{ok, serialize_authenticator(Authenticator)}; NewChain = Chain#chain{authenticators = NAuthenticators},
Result = {ok, serialize_authenticator(Authenticator)},
{ok, Result, NewChain};
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
end end
end. end.
do_authenticate([], _) -> do_authenticate(_ChainName, [], _) ->
{stop, {error, not_authorized}}; {stop, {error, not_authorized}};
do_authenticate([#authenticator{id = ID, provider = Provider, state = State} | More], Credential) -> do_authenticate(
emqx_metrics_worker:inc(authn_metrics, ID, total), ChainName, [#authenticator{id = ID, provider = Provider, state = State} | More], Credential
) ->
MetricsID = metrics_id(ChainName, ID),
emqx_metrics_worker:inc(authn_metrics, MetricsID, total),
try Provider:authenticate(Credential, State) of try Provider:authenticate(Credential, State) of
ignore -> ignore ->
ok = emqx_metrics_worker:inc(authn_metrics, ID, nomatch), ok = emqx_metrics_worker:inc(authn_metrics, MetricsID, nomatch),
do_authenticate(More, Credential); do_authenticate(ChainName, More, Credential);
Result -> Result ->
%% {ok, Extra} %% {ok, Extra}
%% {ok, Extra, AuthData} %% {ok, Extra, AuthData}
@ -644,9 +627,9 @@ do_authenticate([#authenticator{id = ID, provider = Provider, state = State} | M
%% {error, Reason} %% {error, Reason}
case Result of case Result of
{ok, _} -> {ok, _} ->
emqx_metrics_worker:inc(authn_metrics, ID, success); emqx_metrics_worker:inc(authn_metrics, MetricsID, success);
{error, _} -> {error, _} ->
emqx_metrics_worker:inc(authn_metrics, ID, failed); emqx_metrics_worker:inc(authn_metrics, MetricsID, failed);
_ -> _ ->
ok ok
end, end,
@ -660,13 +643,21 @@ do_authenticate([#authenticator{id = ID, provider = Provider, state = State} | M
stacktrace => Stacktrace, stacktrace => Stacktrace,
authenticator => ID authenticator => ID
}), }),
emqx_metrics_worker:inc(authn_metrics, ID, nomatch), emqx_metrics_worker:inc(authn_metrics, MetricsID, nomatch),
do_authenticate(More, Credential) do_authenticate(ChainName, More, Credential)
end. end.
reply(Reply, State) -> reply(Reply, State) ->
{reply, Reply, State}. {reply, Reply, State}.
save_chain(#chain{
name = Name,
authenticators = []
}) ->
ets:delete(?CHAINS_TAB, Name);
save_chain(#chain{} = Chain) ->
ets:insert(?CHAINS_TAB, Chain).
create_chain_table() -> create_chain_table() ->
try try
_ = ets:new(?CHAINS_TAB, [ _ = ets:new(?CHAINS_TAB, [
@ -751,7 +742,7 @@ do_create_authenticator(AuthenticatorID, #{enable := Enable} = Config, Providers
end end
end. end.
do_delete_authenticators(MatchFun, #chain{authenticators = Authenticators} = Chain) -> do_delete_authenticators(MatchFun, #chain{name = Name, authenticators = Authenticators} = Chain) ->
{Matching, Others} = lists:partition(MatchFun, Authenticators), {Matching, Others} = lists:partition(MatchFun, Authenticators),
MatchingIDs = lists:map( MatchingIDs = lists:map(
@ -759,9 +750,14 @@ do_delete_authenticators(MatchFun, #chain{authenticators = Authenticators} = Cha
Matching Matching
), ),
ok = lists:foreach(fun do_destroy_authenticator/1, Matching), ok = lists:foreach(
true = ets:insert(?CHAINS_TAB, Chain#chain{authenticators = Others}), fun(#authenticator{id = ID} = Authenticator) ->
MatchingIDs. do_destroy_authenticator(Authenticator),
emqx_metrics_worker:clear_metrics(authn_metrics, metrics_id(Name, ID))
end,
Matching
),
{MatchingIDs, Chain#chain{authenticators = Others}}.
do_destroy_authenticator(#authenticator{provider = Provider, state = State}) -> do_destroy_authenticator(#authenticator{provider = Provider, state = State}) ->
_ = Provider:destroy(State), _ = Provider:destroy(State),
@ -804,21 +800,41 @@ insert(
insert(Authenticator, [Authenticator0 | More], {Relative, RelatedID}, Acc) -> insert(Authenticator, [Authenticator0 | More], {Relative, RelatedID}, Acc) ->
insert(Authenticator, More, {Relative, RelatedID}, [Authenticator0 | Acc]). insert(Authenticator, More, {Relative, RelatedID}, [Authenticator0 | Acc]).
update_chain(ChainName, UpdateFun) -> with_new_chain(ChainName, Fun) ->
case ets:lookup(?CHAINS_TAB, ChainName) of
[] ->
Chain = #chain{name = ChainName, authenticators = []},
do_with_chain(Fun, Chain);
[Chain] ->
do_with_chain(Fun, Chain)
end.
with_chain(ChainName, Fun) ->
case ets:lookup(?CHAINS_TAB, ChainName) of case ets:lookup(?CHAINS_TAB, ChainName) of
[] -> [] ->
{error, {not_found, {chain, ChainName}}}; {error, {not_found, {chain, ChainName}}};
[Chain] -> [Chain] ->
do_with_chain(Fun, Chain)
end.
do_with_chain(Fun, Chain) ->
try try
UpdateFun(Chain) case Fun(Chain) of
{ok, Result} ->
Result;
{ok, Result, NewChain} ->
save_chain(NewChain),
Result;
{error, _} = Error ->
Error
end
catch catch
Class:Reason:Stk -> Class:Reason:Stk ->
{error, {exception, {Class, Reason, Stk}}} {error, {exception, {Class, Reason, Stk}}}
end
end. end.
call_authenticator(ChainName, AuthenticatorID, Func, Args) -> call_authenticator(ChainName, AuthenticatorID, Func, Args) ->
UpdateFun = Fun =
fun(#chain{authenticators = Authenticators}) -> fun(#chain{authenticators = Authenticators}) ->
case lists:keyfind(AuthenticatorID, #authenticator.id, Authenticators) of case lists:keyfind(AuthenticatorID, #authenticator.id, Authenticators) of
false -> false ->
@ -826,13 +842,13 @@ call_authenticator(ChainName, AuthenticatorID, Func, Args) ->
#authenticator{provider = Provider, state = State} -> #authenticator{provider = Provider, state = State} ->
case erlang:function_exported(Provider, Func, length(Args) + 1) of case erlang:function_exported(Provider, Func, length(Args) + 1) of
true -> true ->
erlang:apply(Provider, Func, Args ++ [State]); {ok, erlang:apply(Provider, Func, Args ++ [State])};
false -> false ->
{error, unsupported_operation} {error, unsupported_operation}
end end
end end
end, end,
update_chain(ChainName, UpdateFun). with_chain(ChainName, Fun).
serialize_chain(#chain{ serialize_chain(#chain{
name = Name, name = Name,
@ -875,6 +891,9 @@ insert_user_group(
insert_user_group(_Chain, Config) -> insert_user_group(_Chain, Config) ->
Config. Config.
metrics_id(ChainName, AuthenticatorId) ->
iolist_to_binary([atom_to_binary(ChainName), <<"-">>, AuthenticatorId]).
to_list(undefined) -> []; to_list(undefined) -> [];
to_list(M) when M =:= #{} -> []; to_list(M) when M =:= #{} -> [];
to_list(M) when is_map(M) -> [M]; to_list(M) when is_map(M) -> [M];

View File

@ -52,7 +52,6 @@
-type update_request() :: -type update_request() ::
{create_authenticator, chain_name(), map()} {create_authenticator, chain_name(), map()}
| {delete_authenticator, chain_name(), authenticator_id()} | {delete_authenticator, chain_name(), authenticator_id()}
| {delete_authenticators, chain_name()}
| {update_authenticator, chain_name(), authenticator_id(), map()} | {update_authenticator, chain_name(), authenticator_id(), map()}
| {move_authenticator, chain_name(), authenticator_id(), position()}. | {move_authenticator, chain_name(), authenticator_id(), position()}.
@ -89,8 +88,6 @@ do_pre_config_update({delete_authenticator, _ChainName, AuthenticatorID}, OldCon
OldConfig OldConfig
), ),
{ok, NewConfig}; {ok, NewConfig};
do_pre_config_update({delete_authenticators, _ChainName}, _OldConfig) ->
{ok, []};
do_pre_config_update({update_authenticator, ChainName, AuthenticatorID, Config}, OldConfig) -> do_pre_config_update({update_authenticator, ChainName, AuthenticatorID, Config}, OldConfig) ->
CertsDir = certs_dir(ChainName, AuthenticatorID), CertsDir = certs_dir(ChainName, AuthenticatorID),
NewConfig = lists:map( NewConfig = lists:map(
@ -143,7 +140,6 @@ post_config_update(_, UpdateReq, NewConfig, OldConfig, AppEnvs) ->
do_post_config_update({create_authenticator, ChainName, Config}, NewConfig, _OldConfig, _AppEnvs) -> do_post_config_update({create_authenticator, ChainName, Config}, NewConfig, _OldConfig, _AppEnvs) ->
NConfig = get_authenticator_config(authenticator_id(Config), NewConfig), NConfig = get_authenticator_config(authenticator_id(Config), NewConfig),
_ = emqx_authentication:create_chain(ChainName),
emqx_authentication:create_authenticator(ChainName, NConfig); emqx_authentication:create_authenticator(ChainName, NConfig);
do_post_config_update( do_post_config_update(
{delete_authenticator, ChainName, AuthenticatorID}, {delete_authenticator, ChainName, AuthenticatorID},
@ -159,25 +155,6 @@ do_post_config_update(
{error, Reason} -> {error, Reason} ->
{error, Reason} {error, Reason}
end; end;
do_post_config_update(
{delete_authenticators, ChainName},
_NewConfig,
OldConfig,
_AppEnvs
) ->
case emqx_authentication:delete_chain(ChainName) of
ok ->
lists:foreach(
fun(Config) ->
AuthenticatorID = authenticator_id(Config),
CertsDir = certs_dir(ChainName, AuthenticatorID),
ok = clear_certs(CertsDir, Config)
end,
to_list(OldConfig)
);
{error, Reason} ->
{error, Reason}
end;
do_post_config_update( do_post_config_update(
{update_authenticator, ChainName, AuthenticatorID, Config}, {update_authenticator, ChainName, AuthenticatorID, Config},
NewConfig, NewConfig,

View File

@ -113,20 +113,32 @@ end_per_testcase(Case, Config) ->
_ = ?MODULE:Case({'end', Config}), _ = ?MODULE:Case({'end', Config}),
ok. ok.
t_chain({_, Config}) -> t_chain({'init', Config}) ->
Config; Config;
t_chain(Config) when is_list(Config) -> t_chain(Config) when is_list(Config) ->
% CRUD of authentication chain % CRUD of authentication chain
ChainName = 'test', ChainName = 'test',
?assertMatch({ok, []}, ?AUTHN:list_chains()), ?assertMatch({ok, []}, ?AUTHN:list_chains()),
?assertMatch({ok, []}, ?AUTHN:list_chain_names()), ?assertMatch({ok, []}, ?AUTHN:list_chain_names()),
?assertMatch({ok, #{name := ChainName, authenticators := []}}, ?AUTHN:create_chain(ChainName)),
?assertEqual({error, {already_exists, {chain, ChainName}}}, ?AUTHN:create_chain(ChainName)), %% to create a chain we need create an authenticator
?assertMatch({ok, #{name := ChainName, authenticators := []}}, ?AUTHN:lookup_chain(ChainName)), AuthenticatorConfig = #{
mechanism => password_based,
backend => built_in_database,
enable => true
},
register_provider({password_based, built_in_database}, ?MODULE),
?AUTHN:create_authenticator(ChainName, AuthenticatorConfig),
?assertMatch({ok, #{name := ChainName, authenticators := [_]}}, ?AUTHN:lookup_chain(ChainName)),
?assertMatch({ok, [#{name := ChainName}]}, ?AUTHN:list_chains()), ?assertMatch({ok, [#{name := ChainName}]}, ?AUTHN:list_chains()),
?assertEqual({ok, [ChainName]}, ?AUTHN:list_chain_names()), ?assertEqual({ok, [ChainName]}, ?AUTHN:list_chain_names()),
?assertEqual(ok, ?AUTHN:delete_chain(ChainName)), ?assertEqual(ok, ?AUTHN:delete_chain(ChainName)),
?assertMatch({error, {not_found, {chain, ChainName}}}, ?AUTHN:lookup_chain(ChainName)), ?assertMatch({error, {not_found, {chain, ChainName}}}, ?AUTHN:lookup_chain(ChainName)),
ok;
t_chain({'end', _Config}) ->
?AUTHN:delete_chain(test),
?AUTHN:deregister_providers([{password_based, built_in_database}]),
ok. ok.
t_authenticator({'init', Config}) -> t_authenticator({'init', Config}) ->
@ -143,13 +155,6 @@ t_authenticator(Config) when is_list(Config) ->
enable => true enable => true
}, },
% Create an authenticator when the authentication chain does not exist
?assertEqual(
{error, {not_found, {chain, ChainName}}},
?AUTHN:create_authenticator(ChainName, AuthenticatorConfig1)
),
?AUTHN:create_chain(ChainName),
% Create an authenticator when the provider does not exist % Create an authenticator when the provider does not exist
?assertEqual( ?assertEqual(
@ -183,11 +188,14 @@ t_authenticator(Config) when is_list(Config) ->
?assertEqual(ok, ?AUTHN:delete_authenticator(ChainName, ID1)), ?assertEqual(ok, ?AUTHN:delete_authenticator(ChainName, ID1)),
?assertEqual( ?assertEqual(
{error, {not_found, {authenticator, ID1}}}, {error, {not_found, {chain, test}}},
?AUTHN:update_authenticator(ChainName, ID1, AuthenticatorConfig1) ?AUTHN:update_authenticator(ChainName, ID1, AuthenticatorConfig1)
), ),
?assertMatch({ok, []}, ?AUTHN:list_authenticators(ChainName)), ?assertMatch(
{error, {not_found, {chain, ChainName}}},
?AUTHN:list_authenticators(ChainName)
),
% Multiple authenticators exist at the same time % Multiple authenticators exist at the same time
AuthNType2 = ?config("auth2"), AuthNType2 = ?config("auth2"),
@ -253,7 +261,6 @@ t_authenticate(Config) when is_list(Config) ->
backend => built_in_database, backend => built_in_database,
enable => true enable => true
}, },
?AUTHN:create_chain(ListenerID),
?assertMatch({ok, _}, ?AUTHN:create_authenticator(ListenerID, AuthenticatorConfig)), ?assertMatch({ok, _}, ?AUTHN:create_authenticator(ListenerID, AuthenticatorConfig)),
?assertEqual( ?assertEqual(
@ -352,7 +359,7 @@ t_update_config(Config) when is_list(Config) ->
), ),
?assertEqual( ?assertEqual(
{error, {not_found, {authenticator, ID2}}}, {error, {not_found, {chain, Global}}},
?AUTHN:lookup_authenticator(Global, ID2) ?AUTHN:lookup_authenticator(Global, ID2)
), ),
@ -427,7 +434,15 @@ t_restart({'init', Config}) ->
t_restart(Config) when is_list(Config) -> t_restart(Config) when is_list(Config) ->
?assertEqual({ok, []}, ?AUTHN:list_chain_names()), ?assertEqual({ok, []}, ?AUTHN:list_chain_names()),
?AUTHN:create_chain(test_chain), %% to create a chain we need create an authenticator
AuthenticatorConfig = #{
mechanism => password_based,
backend => built_in_database,
enable => true
},
register_provider({password_based, built_in_database}, ?MODULE),
?AUTHN:create_authenticator(test_chain, AuthenticatorConfig),
?assertEqual({ok, [test_chain]}, ?AUTHN:list_chain_names()), ?assertEqual({ok, [test_chain]}, ?AUTHN:list_chain_names()),
ok = supervisor:terminate_child(emqx_authentication_sup, ?AUTHN), ok = supervisor:terminate_child(emqx_authentication_sup, ?AUTHN),
@ -436,6 +451,7 @@ t_restart(Config) when is_list(Config) ->
?assertEqual({ok, [test_chain]}, ?AUTHN:list_chain_names()); ?assertEqual({ok, [test_chain]}, ?AUTHN:list_chain_names());
t_restart({'end', _Config}) -> t_restart({'end', _Config}) ->
?AUTHN:delete_chain(test_chain), ?AUTHN:delete_chain(test_chain),
?AUTHN:deregister_providers([{password_based, built_in_database}]),
ok. ok.
t_convert_certs({_, Config}) -> t_convert_certs({_, Config}) ->

View File

@ -49,13 +49,6 @@ emqx_authn_api {
} }
} }
listeners_listener_id_authentication_delete {
desc {
en: """Delete listener-specific authentication."""
zh: """删除特定于侦听器的身份验证。"""
}
}
listeners_listener_id_authentication_post { listeners_listener_id_authentication_post {
desc { desc {
en: """Create authenticator for listener authentication.""" en: """Create authenticator for listener authentication."""

View File

@ -248,15 +248,6 @@ schema("/listeners/:listener_id/authentication") ->
) )
} }
}, },
delete => #{
tags => ?API_TAGS_SINGLE,
description => ?DESC(listeners_listener_id_authentication_delete),
parameters => [param_listener_id()],
responses => #{
204 => <<"Authentication chain deleted">>,
404 => error_codes([?NOT_FOUND], <<"Not Found">>)
}
},
post => #{ post => #{
tags => ?API_TAGS_SINGLE, tags => ?API_TAGS_SINGLE,
description => ?DESC(listeners_listener_id_authentication_post), description => ?DESC(listeners_listener_id_authentication_post),
@ -610,13 +601,6 @@ listener_authenticators(get, #{bindings := #{listener_id := ListenerID}}) ->
fun(Type, Name, _) -> fun(Type, Name, _) ->
list_authenticators([listeners, Type, Name, authentication]) list_authenticators([listeners, Type, Name, authentication])
end end
);
listener_authenticators(delete, #{bindings := #{listener_id := ListenerID}}) ->
with_listener(
ListenerID,
fun(Type, Name, ChainName) ->
delete_authenticators([listeners, Type, Name, authentication], ChainName)
end
). ).
listener_authenticator(get, #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}}) -> listener_authenticator(get, #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}}) ->
@ -879,7 +863,8 @@ lookup_from_local_node(ChainName, AuthenticatorID) ->
NodeId = node(self()), NodeId = node(self()),
case emqx_authentication:lookup_authenticator(ChainName, AuthenticatorID) of case emqx_authentication:lookup_authenticator(ChainName, AuthenticatorID) of
{ok, #{provider := Provider, state := State}} -> {ok, #{provider := Provider, state := State}} ->
Metrics = emqx_metrics_worker:get_metrics(authn_metrics, AuthenticatorID), MetricsId = emqx_authentication:metrics_id(ChainName, AuthenticatorID),
Metrics = emqx_metrics_worker:get_metrics(authn_metrics, MetricsId),
case lists:member(Provider, resource_provider()) of case lists:member(Provider, resource_provider()) of
false -> false ->
{ok, {NodeId, connected, Metrics, #{}}}; {ok, {NodeId, connected, Metrics, #{}}};
@ -1036,16 +1021,6 @@ delete_authenticator(ConfKeyPath, ChainName, AuthenticatorID) ->
serialize_error(Reason) serialize_error(Reason)
end. end.
delete_authenticators(ConfKeyPath, ChainName) ->
case update_config(ConfKeyPath, {delete_authenticators, ChainName}) of
{ok, _} ->
{204};
{error, {_PrePostConfigUpdate, emqx_authentication, Reason}} ->
serialize_error(Reason);
{error, Reason} ->
serialize_error(Reason)
end.
move_authenticator(ConfKeyPath, ChainName, AuthenticatorID, Position) -> move_authenticator(ConfKeyPath, ChainName, AuthenticatorID, Position) ->
case parse_position(Position) of case parse_position(Position) of
{ok, NPosition} -> {ok, NPosition} ->

View File

@ -674,6 +674,11 @@ t_switch_to_global_chain(_) ->
uri([listeners, "tcp:default", ?CONF_NS]), uri([listeners, "tcp:default", ?CONF_NS]),
emqx_authn_test_lib:built_in_database_example() emqx_authn_test_lib:built_in_database_example()
), ),
{ok, 200, _} = request(
post,
uri([listeners, "tcp:default", ?CONF_NS]),
maps:put(enable, false, emqx_authn_test_lib:http_example())
),
GlobalUser = #{user_id => <<"global_user">>, password => <<"p1">>}, GlobalUser = #{user_id => <<"global_user">>, password => <<"p1">>},
@ -716,29 +721,46 @@ t_switch_to_global_chain(_) ->
{ok, 204, _} = request( {ok, 204, _} = request(
delete, delete,
uri([listeners, "tcp:default", ?CONF_NS]) uri([listeners, "tcp:default", ?CONF_NS, "password_based:built_in_database"])
), ),
%% Listener user should not be OK local chain removed %% Now listener has only disabled authenticators, should allow anonymous access
{ok, Client2} = emqtt:start_link([ {ok, Client2} = emqtt:start_link([
{username, <<"any_user">>},
{password, <<"any_password">>}
]),
?assertMatch(
{ok, _},
emqtt:connect(Client2)
),
ok = emqtt:disconnect(Client2),
{ok, 204, _} = request(
delete,
uri([listeners, "tcp:default", ?CONF_NS, "password_based:http"])
),
%% Local chain is empty now and should be removed
%% Listener user should not be OK
{ok, Client3} = emqtt:start_link([
{username, <<"listener_user">>}, {username, <<"listener_user">>},
{password, <<"p1">>} {password, <<"p1">>}
]), ]),
?assertMatch( ?assertMatch(
{error, {unauthorized_client, _}}, {error, {unauthorized_client, _}},
emqtt:connect(Client2) emqtt:connect(Client3)
), ),
%% Global user should be now OK, switched back to the global chain %% Global user should be now OK, switched back to the global chain
{ok, Client3} = emqtt:start_link([ {ok, Client4} = emqtt:start_link([
{username, <<"global_user">>}, {username, <<"global_user">>},
{password, <<"p1">>} {password, <<"p1">>}
]), ]),
?assertMatch( ?assertMatch(
{ok, _}, {ok, _},
emqtt:connect(Client3) emqtt:connect(Client4)
), ),
ok = emqtt:disconnect(Client3). ok = emqtt:disconnect(Client4).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% Helpers %% Helpers

View File

@ -101,7 +101,10 @@ t_create_invalid(_Config) ->
throw:Error -> throw:Error ->
{error, Error} {error, Error}
end, end,
{ok, []} = emqx_authentication:list_authenticators(?GLOBAL) ?assertEqual(
{error, {not_found, {chain, ?GLOBAL}}},
emqx_authentication:list_authenticators(?GLOBAL)
)
end, end,
InvalidConfigs InvalidConfigs
). ).

View File

@ -95,7 +95,10 @@ t_create_invalid(_Config) ->
{create_authenticator, ?GLOBAL, Config} {create_authenticator, ?GLOBAL, Config}
), ),
{ok, []} = emqx_authentication:list_authenticators(?GLOBAL) ?assertEqual(
{error, {not_found, {chain, ?GLOBAL}}},
emqx_authentication:list_authenticators(?GLOBAL)
)
end, end,
InvalidConfigs InvalidConfigs
). ).

View File

@ -113,7 +113,10 @@ t_create_invalid(_Config) ->
{create_authenticator, ?GLOBAL, Config} {create_authenticator, ?GLOBAL, Config}
), ),
emqx_authn_test_lib:delete_config(?ResourceID), emqx_authn_test_lib:delete_config(?ResourceID),
{ok, _} = emqx_authentication:list_authenticators(?GLOBAL) ?assertEqual(
{error, {not_found, {chain, ?GLOBAL}}},
emqx_authentication:list_authenticators(?GLOBAL)
)
end, end,
InvalidConfigs InvalidConfigs
). ).

View File

@ -114,7 +114,10 @@ t_create_invalid(_Config) ->
{create_authenticator, ?GLOBAL, Config} {create_authenticator, ?GLOBAL, Config}
), ),
emqx_authn_test_lib:delete_config(?ResourceID), emqx_authn_test_lib:delete_config(?ResourceID),
{ok, []} = emqx_authentication:list_authenticators(?GLOBAL) ?assertEqual(
{error, {not_found, {chain, ?GLOBAL}}},
emqx_authentication:list_authenticators(?GLOBAL)
)
end, end,
InvalidConfigs InvalidConfigs
). ).

View File

@ -85,8 +85,10 @@ end_per_suite(_Config) ->
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
t_create(_Config) -> t_create(_Config) ->
{ok, []} = emqx_authentication:list_authenticators(?GLOBAL), ?assertEqual(
{error, {not_found, {chain, ?GLOBAL}}},
emqx_authentication:list_authenticators(?GLOBAL)
),
AuthConfig = raw_redis_auth_config(), AuthConfig = raw_redis_auth_config(),
{ok, _} = emqx:update_config( {ok, _} = emqx:update_config(
?PATH, ?PATH,
@ -119,7 +121,10 @@ t_create_invalid(_Config) ->
{create_authenticator, ?GLOBAL, Config} {create_authenticator, ?GLOBAL, Config}
), ),
{ok, []} = emqx_authentication:list_authenticators(?GLOBAL) ?assertEqual(
{error, {not_found, {chain, ?GLOBAL}}},
emqx_authentication:list_authenticators(?GLOBAL)
)
end, end,
InvalidConfigs InvalidConfigs
), ),
@ -139,7 +144,10 @@ t_create_invalid(_Config) ->
{create_authenticator, ?GLOBAL, Config} {create_authenticator, ?GLOBAL, Config}
), ),
emqx_authn_test_lib:delete_config(?ResourceID), emqx_authn_test_lib:delete_config(?ResourceID),
{ok, []} = emqx_authentication:list_authenticators(?GLOBAL) ?assertEqual(
{error, {not_found, {chain, ?GLOBAL}}},
emqx_authentication:list_authenticators(?GLOBAL)
)
end, end,
InvalidConfigs1 InvalidConfigs1
). ).

View File

@ -87,7 +87,10 @@ t_create_invalid(_Config) ->
{create_authenticator, ?GLOBAL, InvalidConfig} {create_authenticator, ?GLOBAL, InvalidConfig}
), ),
{ok, []} = emqx_authentication:list_authenticators(?GLOBAL). ?assertEqual(
{error, {not_found, {chain, ?GLOBAL}}},
emqx_authentication:list_authenticators(?GLOBAL)
).
t_authenticate(_Config) -> t_authenticate(_Config) ->
Algorithm = sha512, Algorithm = sha512,

View File

@ -277,8 +277,6 @@ authn_conf(Conf) ->
maps:get(authentication, Conf, #{enable => false}). maps:get(authentication, Conf, #{enable => false}).
do_create_authn_chain(ChainName, AuthConf) -> do_create_authn_chain(ChainName, AuthConf) ->
case ensure_chain(ChainName) of
ok ->
case emqx_authentication:create_authenticator(ChainName, AuthConf) of case emqx_authentication:create_authenticator(ChainName, AuthConf) of
{ok, _} -> {ok, _} ->
ok; ok;
@ -290,24 +288,6 @@ do_create_authn_chain(ChainName, AuthConf) ->
config => AuthConf config => AuthConf
}), }),
throw({badauth, Reason}) throw({badauth, Reason})
end;
{error, Reason} ->
?SLOG(error, #{
msg => "failed_to_create_authn_chanin",
chain_name => ChainName,
reason => Reason
}),
throw({badauth, Reason})
end.
ensure_chain(ChainName) ->
case emqx_authentication:create_chain(ChainName) of
{ok, _ChainInfo} ->
ok;
{error, {already_exists, _}} ->
ok;
{error, Reason} ->
{error, Reason}
end. end.
do_deinit_authn(Names) -> do_deinit_authn(Names) ->