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

feat(authn hot config): initial support for hot config

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

+ 100 - 2
apps/emqx_authn/src/emqx_authn.erl

@@ -16,8 +16,17 @@
 
 -module(emqx_authn).
 
+-behaviour(emqx_config_handler).
+
 -include("emqx_authn.hrl").
 
+-export([mnesia/1]).
+
+-export([ pre_config_update/2
+        , post_config_update/3
+        , update_config/2
+        ]).
+
 -export([ enable/0
         , disable/0
         , is_enabled/0
@@ -46,8 +55,6 @@
         , list_users/2
         ]).
 
--export([mnesia/1]).
-
 -boot_mnesia({mnesia, [boot]}).
 -copy_mnesia({mnesia, [copy]}).
 
@@ -75,6 +82,97 @@ mnesia(boot) ->
 mnesia(copy) ->
     ok = ekka_mnesia:copy_table(?CHAIN_TAB, ram_copies).
 
+%%------------------------------------------------------------------------------
+%% APIs
+%%------------------------------------------------------------------------------
+
+pre_config_update({enable, Enable}, _OldConfig) ->
+    Enable;
+pre_config_update({create_authenticator, Config}, OldConfig) ->
+    OldConfig ++ [Config];
+pre_config_update({delete_authenticator, ID}, OldConfig) ->
+    case lookup_authenticator(?CHAIN, ID) of
+        {error, Reason} -> error(Reason);
+        {ok, #{name := Name}} ->
+            lists:filter(fun(#{<<"name">> := N}) ->
+                             N =/= Name
+                         end, OldConfig)
+    end;
+pre_config_update({update_authenticator, ID, Config}, OldConfig) ->
+    case lookup_authenticator(?CHAIN, ID) of
+        {error, Reason} -> error(Reason);
+        {ok, #{name := Name}} ->
+            lists:map(fun(#{<<"name">> := N} = C) ->
+                          case N =:= Name of
+                              true -> Config;
+                              false -> C
+                          end
+                      end, OldConfig)
+    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)
+    end.
+
+post_config_update({enable, true}, _NewConfig, _OldConfig) ->
+    emqx_authn:enable();
+post_config_update({enable, false}, _NewConfig, _OldConfig) ->
+    emqx_authn:disable();
+post_config_update({create_authenticator, #{<<"name">> := Name}}, NewConfig, _OldConfig) ->
+    case lists:filter(
+             fun(#{name := N}) ->
+                 N =:= Name
+             end, NewConfig) of
+        [Config] ->
+            case create_authenticator(?CHAIN, Config) of
+                {ok, _} -> ok;
+                {error, Reason} -> throw(Reason)
+            end;
+        [_Config | _] ->
+            error(name_has_be_used)
+    end;
+post_config_update({delete_authenticator, ID}, _NewConfig, _OldConfig) ->
+    case delete_authenticator(?CHAIN, ID) of
+        ok -> ok;
+        {error, Reason} -> throw(Reason)
+    end;
+post_config_update({update_authenticator, ID, #{<<"name">> := Name}}, NewConfig, _OldConfig) ->
+    case lists:filter(
+             fun(#{name := N}) ->
+                 N =:= Name
+             end, NewConfig) of
+        [Config] ->
+            case update_authenticator(?CHAIN, ID, Config) of
+                {ok, _} -> ok;
+                {error, Reason} -> throw(Reason)
+            end;
+        [_Config | _] ->
+            error(name_has_be_used)
+    end;
+post_config_update({update_or_create_authenticator, ID, #{<<"name">> := Name}}, NewConfig, _OldConfig) ->
+    case lists:filter(
+             fun(#{name := N}) ->
+                 N =:= Name
+             end, NewConfig) of
+        [Config] ->
+            case update_or_create_authenticator(?CHAIN, ID, Config) of
+                {ok, _} -> ok;
+                {error, Reason} -> throw(Reason)
+            end;
+        [_Config | _] ->
+            error(name_has_be_used)
+    end.
+
+update_config(Path, ConfigRequest) ->
+    emqx_config:update(emqx_authn_schema, Path, ConfigRequest).
+
 enable() ->
     case emqx:hook('client.authenticate', {?MODULE, authenticate, []}) of
         ok -> ok;

+ 16 - 32
apps/emqx_authn/src/emqx_authn_api.erl

@@ -1253,14 +1253,9 @@ definitions() ->
 authentication(post, Request) ->
     {ok, Body, _} = cowboy_req:read_body(Request),
     case emqx_json:decode(Body, [return_maps]) of
-        #{<<"enable">> := true} ->
-            ok = emqx_authn:enable(),
+        #{<<"enable">> := Enable} ->
+            emqx_authn:update_config([authentication, enable], {enable, Enable}),
             {204};
-        #{<<"enable">> := false} ->
-            ok = emqx_authn:disable(),
-            {204};
-        #{<<"enable">> := _} ->
-            serialize_error({invalid_parameter, enable});
         _ ->
             serialize_error({missing_parameter, enable})
     end;
@@ -1270,16 +1265,10 @@ authentication(get, _Request) ->
 
 authenticators(post, Request) ->
     {ok, Body, _} = cowboy_req:read_body(Request),
-    AuthenticatorConfig = emqx_json:decode(Body, [return_maps]),
-    Config = #{<<"authentication">> => #{
-                   <<"authenticators">> => [AuthenticatorConfig]
-               }},
-    NConfig = hocon_schema:check_plain(emqx_authn_schema, Config,
-                                       #{nullable => true}),
-    #{authentication := #{authenticators := [NAuthenticatorConfig]}} = emqx_map_lib:unsafe_atom_key_map(NConfig),
-    case emqx_authn:create_authenticator(?CHAIN, NAuthenticatorConfig) of
-        {ok, Authenticator2} ->
-            {201, Authenticator2};
+    Config = emqx_json:decode(Body, [return_maps]),
+    case emqx_authn:update_config([authentication, authenticators], {create_authenticator, Config}) of
+        ok ->    
+            {204};
         {error, Reason} ->
             serialize_error(Reason)
     end;
@@ -1298,22 +1287,17 @@ authenticators2(get, Request) ->
 authenticators2(put, Request) ->
     AuthenticatorID = cowboy_req:binding(id, Request),
     {ok, Body, _} = cowboy_req:read_body(Request),
-    AuthenticatorConfig = emqx_json:decode(Body, [return_maps]),
-    Config = #{<<"authentication">> => #{
-                   <<"authenticators">> => [AuthenticatorConfig]
-               }},
-    NConfig = hocon_schema:check_plain(emqx_authn_schema, Config,
-                                       #{nullable => true}),
-    #{authentication := #{authenticators := [NAuthenticatorConfig]}} = emqx_map_lib:unsafe_atom_key_map(NConfig),
-    case emqx_authn:update_or_create_authenticator(?CHAIN, AuthenticatorID, NAuthenticatorConfig) of
-        {ok, Authenticator} ->
-            {200, Authenticator};
+    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} ->
             serialize_error(Reason)
     end;
 authenticators2(delete, Request) ->
     AuthenticatorID = cowboy_req:binding(id, Request),
-    case emqx_authn:delete_authenticator(?CHAIN, AuthenticatorID) of
+    case emqx_authn:update_config([authentication, authenticators], {delete_authenticator, AuthenticatorID}) of
         ok ->
             {204};
         {error, Reason} ->
@@ -1324,7 +1308,7 @@ position(post, Request) ->
     AuthenticatorID = cowboy_req:binding(id, Request),
     {ok, Body, _} = cowboy_req:read_body(Request),
     NBody = emqx_json:decode(Body, [return_maps]),
-    Config = hocon_schema:check_plain(emqx_authn_other_schema, #{<<"position">> => NBody},
+    Config = hocon_schema:check_plain(emqx_authn_implied_schema, #{<<"position">> => NBody},
                                       #{nullable => true}, ["position"]),
     #{position := #{position := Position}} = emqx_map_lib:unsafe_atom_key_map(Config),
     case emqx_authn:move_authenticator_to_the_nth(?CHAIN, AuthenticatorID, Position) of
@@ -1338,7 +1322,7 @@ import_users(post, Request) ->
     AuthenticatorID = cowboy_req:binding(id, Request),
     {ok, Body, _} = cowboy_req:read_body(Request),
     NBody = emqx_json:decode(Body, [return_maps]),
-    Config = hocon_schema:check_plain(emqx_authn_other_schema, #{<<"filename">> => NBody},
+    Config = hocon_schema:check_plain(emqx_authn_implied_schema, #{<<"filename">> => NBody},
                                       #{nullable => true}, ["filename"]),
     #{filename := #{filename := Filename}} = emqx_map_lib:unsafe_atom_key_map(Config),
     case emqx_authn:import_users(?CHAIN, AuthenticatorID, Filename) of
@@ -1352,7 +1336,7 @@ users(post, Request) ->
     AuthenticatorID = cowboy_req:binding(id, Request),
     {ok, Body, _} = cowboy_req:read_body(Request),
     NBody = emqx_json:decode(Body, [return_maps]),
-    Config = hocon_schema:check_plain(emqx_authn_other_schema, #{<<"user_info">> => NBody},
+    Config = hocon_schema:check_plain(emqx_authn_implied_schema, #{<<"user_info">> => NBody},
                                       #{nullable => true}, ["user_info"]),
     #{user_info := UserInfo} = emqx_map_lib:unsafe_atom_key_map(Config),
     case emqx_authn:add_user(?CHAIN, AuthenticatorID, UserInfo) of
@@ -1375,7 +1359,7 @@ users2(patch, Request) ->
     UserID = cowboy_req:binding(user_id, Request),
     {ok, Body, _} = cowboy_req:read_body(Request),
     NBody = emqx_json:decode(Body, [return_maps]),
-    Config = hocon_schema:check_plain(emqx_authn_other_schema, #{<<"new_user_info">> => NBody},
+    Config = hocon_schema:check_plain(emqx_authn_implied_schema, #{<<"new_user_info">> => NBody},
                                       #{nullable => true}, ["new_user_info"]),
     #{new_user_info := NewUserInfo} = emqx_map_lib:unsafe_atom_key_map(Config),
     case emqx_authn:update_user(?CHAIN, AuthenticatorID, UserID, NewUserInfo) of

+ 1 - 0
apps/emqx_authn/src/emqx_authn_app.erl

@@ -29,6 +29,7 @@
 start(_StartType, _StartArgs) ->
     ok = ekka_rlog:wait_for_shards([?AUTH_SHARD], infinity),
     {ok, Sup} = emqx_authn_sup:start_link(),
+    emqx_config_handler:add_handler([authentication, authenticators], emqx_authn),
     initialize(),
     {ok, Sup}.
 

+ 1 - 1
apps/emqx_authn/src/simple_authn/emqx_authn_other_schema.erl

@@ -14,7 +14,7 @@
 %% limitations under the License.
 %%--------------------------------------------------------------------
 
--module(emqx_authn_other_schema).
+-module(emqx_authn_implied_schema).
 
 -include("emqx_authn.hrl").
 -include_lib("typerefl/include/types.hrl").