diff --git a/apps/emqx_authn/include/emqx_authn.hrl b/apps/emqx_authn/include/emqx_authn.hrl index f9ba7c3b5..c5a392fd0 100644 --- a/apps/emqx_authn/include/emqx_authn.hrl +++ b/apps/emqx_authn/include/emqx_authn.hrl @@ -26,7 +26,6 @@ { id :: binary() , name :: binary() , provider :: module() - , config :: map() , state :: map() }). diff --git a/apps/emqx_authn/src/emqx_authn.erl b/apps/emqx_authn/src/emqx_authn.erl index 384830750..d6644cad5 100644 --- a/apps/emqx_authn/src/emqx_authn.erl +++ b/apps/emqx_authn/src/emqx_authn.erl @@ -76,60 +76,63 @@ %%------------------------------------------------------------------------------ pre_config_update({enable, Enable}, _OldConfig) -> - Enable; + {ok, Enable}; pre_config_update({create_authenticator, Config}, OldConfig) -> - OldConfig ++ [Config]; + {ok, OldConfig ++ [Config]}; pre_config_update({delete_authenticator, ID}, OldConfig) -> case lookup_authenticator(?CHAIN, ID) of - {error, Reason} -> error(Reason); + {error, Reason} -> {error, Reason}; {ok, #{name := Name}} -> - lists:filter(fun(#{<<"name">> := N}) -> - N =/= Name - end, OldConfig) + NewConfig = lists:filter(fun(#{<<"name">> := N}) -> + N =/= Name + end, OldConfig), + {ok, NewConfig} end; pre_config_update({update_authenticator, ID, Config}, OldConfig) -> case lookup_authenticator(?CHAIN, ID) of - {error, Reason} -> error(Reason); + {error, Reason} -> {error, Reason}; {ok, #{name := Name}} -> - lists:map(fun(#{<<"name">> := N} = C) -> - case N =:= Name of - true -> Config; - false -> C - end - end, OldConfig) + NewConfig = lists:map(fun(#{<<"name">> := N} = C) -> + case N =:= Name of + true -> Config; + false -> C + end + end, OldConfig), + {ok, NewConfig} end; pre_config_update({update_or_create_authenticator, ID, Config}, OldConfig) -> case lookup_authenticator(?CHAIN, ID) of {error, _Reason} -> OldConfig ++ [Config]; {ok, #{name := Name}} -> - lists:map(fun(#{<<"name">> := N} = C) -> - case N =:= Name of - true -> Config; - false -> C - end - end, OldConfig) + NewConfig = lists:map(fun(#{<<"name">> := N} = C) -> + case N =:= Name of + true -> Config; + false -> C + end + end, OldConfig), + {ok, NewConfig} end; -pre_config_update({move, ID, Position}, OldConfig) -> +pre_config_update({move_authenticator, ID, Position}, OldConfig) -> case lookup_authenticator(?CHAIN, ID) of - {error, Reason} -> error(Reason); + {error, Reason} -> {error, Reason}; {ok, #{name := Name}} -> {ok, Found, Part1, Part2} = split_by_name(Name, OldConfig), case Position of <<"top">> -> - [Found | Part1] ++ Part2; + {ok, [Found | Part1] ++ Part2}; <<"bottom">> -> - Part1 ++ Part2 ++ [Found]; + {ok, Part1 ++ Part2 ++ [Found]}; Before -> case binary:split(Before, <<":">>, [global]) of [<<"before">>, ID0] -> case lookup_authenticator(?CHAIN, ID0) of - {error, Reason} -> error(Reason); + {error, Reason} -> {error, Reason}; {ok, #{name := Name1}} -> - {ok, NFound, NPart1, NPart2} = split_by_name(Name1, Part1 + Part2), - NPart1 ++ [Found, NFound | NPart2] + {ok, NFound, NPart1, NPart2} = split_by_name(Name1, Part1 ++ Part2), + {ok, NPart1 ++ [Found, NFound | NPart2]} end; _ -> - error({invalid_parameter, position}) + {error, {invalid_parameter, position}} end end end. @@ -144,12 +147,9 @@ post_config_update({create_authenticator, #{<<"name">> := Name}}, NewConfig, _Ol N =:= Name end, NewConfig) of [Config] -> - case create_authenticator(?CHAIN, Config) of - {ok, _} -> ok; - {error, Reason} -> throw(Reason) - end; + create_authenticator(?CHAIN, Config); [_Config | _] -> - error(name_has_be_used) + {error, name_has_be_used} end; post_config_update({delete_authenticator, ID}, _NewConfig, _OldConfig) -> case delete_authenticator(?CHAIN, ID) of @@ -162,12 +162,9 @@ post_config_update({update_authenticator, ID, #{<<"name">> := Name}}, NewConfig, N =:= Name end, NewConfig) of [Config] -> - case update_authenticator(?CHAIN, ID, Config) of - {ok, _} -> ok; - {error, Reason} -> throw(Reason) - end; + update_authenticator(?CHAIN, ID, Config); [_Config | _] -> - error(name_has_be_used) + {error, name_has_be_used} end; post_config_update({update_or_create_authenticator, ID, #{<<"name">> := Name}}, NewConfig, _OldConfig) -> case lists:filter( @@ -175,14 +172,11 @@ post_config_update({update_or_create_authenticator, ID, #{<<"name">> := Name}}, N =:= Name end, NewConfig) of [Config] -> - case update_or_create_authenticator(?CHAIN, ID, Config) of - {ok, _} -> ok; - {error, Reason} -> throw(Reason) - end; + update_or_create_authenticator(?CHAIN, ID, Config); [_Config | _] -> - error(name_has_be_used) + {error, name_has_be_used} end; -post_config_update({move, ID, Position}, _NewConfig, _OldConfig) -> +post_config_update({move_authenticator, ID, Position}, _NewConfig, _OldConfig) -> NPosition = case Position of <<"top">> -> top; <<"bottom">> -> bottom; @@ -191,16 +185,13 @@ post_config_update({move, ID, Position}, _NewConfig, _OldConfig) -> [<<"before">>, ID0] -> {before, ID0}; _ -> - error({invalid_parameter, position}) + {error, {invalid_parameter, position}} end end, - case move_authenticator(?CHAIN, ID, NPosition) of - ok -> ok; - {error, Reason} -> throw(Reason) - end. + move_authenticator(?CHAIN, ID, NPosition). update_config(Path, ConfigRequest) -> - emqx_config:update(emqx_authn_schema, Path, ConfigRequest). + emqx:update_config(Path, ConfigRequest, #{rawconf_with_defaults => true}). enable() -> case emqx:hook('client.authenticate', {?MODULE, authenticate, []}) of @@ -522,7 +513,6 @@ do_create_authenticator(ChainID, AuthenticatorID, #{name := Name} = Config) -> Authenticator = #authenticator{id = AuthenticatorID, name = Name, provider = Provider, - config = Config, state = switch_version(State)}, {ok, Authenticator}; {error, Reason} -> @@ -570,8 +560,7 @@ update_or_create_authenticator(ChainID, AuthenticatorID, #{name := NewName} = Co case Provider:update(Config#{'_unique' => Unique}, State) of {ok, NewState} -> NewAuthenticator = Authenticator#authenticator{name = NewName, - config = Config, - state = switch_version(NewState)}, + state = switch_version(NewState)}, NewAuthenticators = replace_authenticator(AuthenticatorID, NewAuthenticator, Authenticators), true = ets:insert(?CHAIN_TAB, Chain#chain{authenticators = NewAuthenticators}), {ok, serialize_authenticator(NewAuthenticator)}; @@ -583,9 +572,8 @@ update_or_create_authenticator(ChainID, AuthenticatorID, #{name := NewName} = Co case NewProvider:create(Config#{'_unique' => Unique}) of {ok, NewState} -> NewAuthenticator = Authenticator#authenticator{name = NewName, - provider = NewProvider, - config = Config, - state = switch_version(NewState)}, + provider = NewProvider, + state = switch_version(NewState)}, NewAuthenticators = replace_authenticator(AuthenticatorID, NewAuthenticator, Authenticators), true = ets:insert(?CHAIN_TAB, Chain#chain{authenticators = NewAuthenticators}), _ = Provider:destroy(State), @@ -660,5 +648,7 @@ serialize_authenticators(Authenticators) -> [serialize_authenticator(Authenticator) || {_, _, Authenticator} <- Authenticators]. serialize_authenticator(#authenticator{id = ID, - config = Config}) -> - Config#{id => ID}. + name = Name, + provider = Provider, + state = State}) -> + #{id => ID, name => Name, provider => Provider, state => State}. diff --git a/apps/emqx_authn/src/emqx_authn_api.erl b/apps/emqx_authn/src/emqx_authn_api.erl index 97ed94a8b..20a8e2f7d 100644 --- a/apps/emqx_authn/src/emqx_authn_api.erl +++ b/apps/emqx_authn/src/emqx_authn_api.erl @@ -790,6 +790,7 @@ definitions() -> , minirest:ref(<<"password_based_mysql">>) , minirest:ref(<<"password_based_pgsql">>) , minirest:ref(<<"password_based_mongodb">>) + , minirest:ref(<<"password_based_redis">>) , minirest:ref(<<"password_based_http_server">>) ] } @@ -1292,7 +1293,7 @@ authentication(post, Request) -> {ok, Body, _} = cowboy_req:read_body(Request), case emqx_json:decode(Body, [return_maps]) of #{<<"enable">> := Enable} -> - emqx_authn:update_config([authentication, enable], {enable, Enable}), + {ok, _} = emqx_authn:update_config([authentication, enable], {enable, Enable}), {204}; _ -> serialize_error({missing_parameter, enable}) @@ -1305,20 +1306,28 @@ authenticators(post, Request) -> {ok, Body, _} = cowboy_req:read_body(Request), Config = emqx_json:decode(Body, [return_maps]), case emqx_authn:update_config([authentication, authenticators], {create_authenticator, Config}) of - ok -> - {204}; - {error, Reason} -> + {ok, #{post_config_update := #{emqx_authn := #{id := ID, name := Name}}, + raw_config := RawConfig}} -> + [RawConfig1] = [RC || #{<<"name">> := N} = RC <- RawConfig, N =:= Name], + {200, RawConfig1#{id => ID}}; + {error, {_, _, Reason}} -> serialize_error(Reason) end; authenticators(get, _Request) -> + RawConfig = get_raw_config([authentication, authenticators]), {ok, Authenticators} = emqx_authn:list_authenticators(?CHAIN), - {200, Authenticators}. + NAuthenticators = lists:zipwith(fun(#{<<"name">> := Name} = Config, #{id := ID, name := Name}) -> + Config#{id => ID} + end, RawConfig, Authenticators), + {200, NAuthenticators}. authenticators2(get, Request) -> AuthenticatorID = cowboy_req:binding(id, Request), case emqx_authn:lookup_authenticator(?CHAIN, AuthenticatorID) of - {ok, Authenticator} -> - {200, Authenticator}; + {ok, #{id := ID, name := Name}} -> + RawConfig = get_raw_config([authentication, authenticators]), + [RawConfig1] = [RC || #{<<"name">> := N} = RC <- RawConfig, N =:= Name], + {200, RawConfig1#{id => ID}}; {error, Reason} -> serialize_error(Reason) end; @@ -1328,17 +1337,19 @@ authenticators2(put, Request) -> Config = emqx_json:decode(Body, [return_maps]), case emqx_authn:update_config([authentication, authenticators], {update_or_create_authenticator, AuthenticatorID, Config}) of - ok -> - {204}; - {error, Reason} -> + {ok, #{post_config_update := #{emqx_authn := #{id := ID, name := Name}}, + raw_config := RawConfig}} -> + [RawConfig0] = [RC || #{<<"name">> := N} = RC <- RawConfig, N =:= Name], + {200, RawConfig0#{id => ID}}; + {error, {_, _, Reason}} -> serialize_error(Reason) end; authenticators2(delete, Request) -> AuthenticatorID = cowboy_req:binding(id, Request), case emqx_authn:update_config([authentication, authenticators], {delete_authenticator, AuthenticatorID}) of - ok -> + {ok, _} -> {204}; - {error, Reason} -> + {error, {_, _, Reason}} -> serialize_error(Reason) end. @@ -1348,8 +1359,8 @@ move(post, Request) -> case emqx_json:decode(Body, [return_maps]) of #{<<"position">> := Position} -> case emqx_authn:update_config([authentication, authenticators], {move_authenticator, AuthenticatorID, Position}) of - ok -> {204}; - {error, Reason} -> serialize_error(Reason) + {ok, _} -> {204}; + {error, {_, _, Reason}} -> serialize_error(Reason) end; _ -> serialize_error({missing_parameter, position}) @@ -1361,10 +1372,8 @@ import_users(post, Request) -> case emqx_json:decode(Body, [return_maps]) of #{<<"filename">> := Filename} -> case emqx_authn:import_users(?CHAIN, AuthenticatorID, Filename) of - ok -> - {204}; - {error, Reason} -> - serialize_error(Reason) + ok -> {204}; + {error, Reason} -> serialize_error(Reason) end; _ -> serialize_error({missing_parameter, filename}) @@ -1435,6 +1444,11 @@ users2(delete, Request) -> serialize_error(Reason) end. +get_raw_config(ConfKeyPath) -> + %% TODO: call emqx_config:get_raw(ConfKeyPath) directly + NConfKeyPath = [atom_to_binary(Key, utf8) || Key <- ConfKeyPath], + emqx_map_lib:deep_get(NConfKeyPath, emqx_config:fill_defaults(emqx_config:get_raw([]))). + serialize_error({not_found, {authenticator, ID}}) -> {404, #{code => <<"NOT_FOUND">>, message => list_to_binary(io_lib:format("Authenticator '~s' does not exist", [ID]))}}; diff --git a/apps/emqx_authn/test/emqx_authn_SUITE.erl b/apps/emqx_authn/test/emqx_authn_SUITE.erl index bf6447aba..0be04d6cf 100644 --- a/apps/emqx_authn/test/emqx_authn_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_SUITE.erl @@ -71,7 +71,7 @@ t_authenticator(_) -> secret => <<"abcdef">>, secret_base64_encoded => false, verify_claims => []}, - {ok, #{name := AuthenticatorName1, id := ID1, mechanism := jwt}} = ?AUTH:update_authenticator(?CHAIN, ID1, AuthenticatorConfig2), + {ok, #{name := AuthenticatorName1, id := ID1}} = ?AUTH:update_authenticator(?CHAIN, ID1, AuthenticatorConfig2), ID2 = <<"random">>, ?assertEqual({error, {not_found, {authenticator, ID2}}}, ?AUTH:update_authenticator(?CHAIN, ID2, AuthenticatorConfig2)), @@ -79,9 +79,9 @@ t_authenticator(_) -> AuthenticatorName2 = <<"myauthenticator2">>, AuthenticatorConfig3 = AuthenticatorConfig2#{name => AuthenticatorName2}, - {ok, #{name := AuthenticatorName2, id := ID2, secret := <<"abcdef">>}} = ?AUTH:update_or_create_authenticator(?CHAIN, ID2, AuthenticatorConfig3), + {ok, #{name := AuthenticatorName2, id := ID2}} = ?AUTH:update_or_create_authenticator(?CHAIN, ID2, AuthenticatorConfig3), ?assertMatch({ok, #{name := AuthenticatorName2}}, ?AUTH:lookup_authenticator(?CHAIN, ID2)), - {ok, #{name := AuthenticatorName2, id := ID2, secret := <<"fedcba">>}} = ?AUTH:update_or_create_authenticator(?CHAIN, ID2, AuthenticatorConfig3#{secret := <<"fedcba">>}), + {ok, #{name := AuthenticatorName2, id := ID2}} = ?AUTH:update_or_create_authenticator(?CHAIN, ID2, AuthenticatorConfig3#{secret := <<"fedcba">>}), ?assertMatch({ok, #{id := ?CHAIN, authenticators := [#{name := AuthenticatorName1}, #{name := AuthenticatorName2}]}}, ?AUTH:lookup_chain(?CHAIN)), ?assertMatch({ok, [#{name := AuthenticatorName1}, #{name := AuthenticatorName2}]}, ?AUTH:list_authenticators(?CHAIN)),