Просмотр исходного кода

feat(auth): support hot config

zhouzb 4 лет назад
Родитель
Сommit
e5892d16e5

+ 0 - 1
apps/emqx_authn/include/emqx_authn.hrl

@@ -26,7 +26,6 @@
         { id :: binary()
         , name :: binary()
         , provider :: module()
-        , config :: map()
         , state :: map()
         }).
 

+ 47 - 57
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}.

+ 32 - 18
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]))}};

+ 3 - 3
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)),