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

Merge remote-tracking branch 'ce/master' into authn-import-users-request

JianBo He 3 лет назад
Родитель
Сommit
96f58d0ec8

+ 22 - 0
apps/emqx/src/emqx_authentication_config.erl

@@ -52,6 +52,7 @@
 -type update_request() ::
     {create_authenticator, chain_name(), map()}
     | {delete_authenticator, chain_name(), authenticator_id()}
+    | {delete_authenticators, chain_name()}
     | {update_authenticator, chain_name(), authenticator_id(), map()}
     | {move_authenticator, chain_name(), authenticator_id(), position()}.
 
@@ -88,6 +89,8 @@ do_pre_config_update({delete_authenticator, _ChainName, AuthenticatorID}, OldCon
         OldConfig
     ),
     {ok, NewConfig};
+do_pre_config_update({delete_authenticators, _ChainName}, _OldConfig) ->
+    {ok, []};
 do_pre_config_update({update_authenticator, ChainName, AuthenticatorID, Config}, OldConfig) ->
     CertsDir = certs_dir(ChainName, AuthenticatorID),
     NewConfig = lists:map(
@@ -156,6 +159,25 @@ do_post_config_update(
         {error, Reason} ->
             {error, Reason}
     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(
     {update_authenticator, ChainName, AuthenticatorID, Config},
     NewConfig,

+ 7 - 0
apps/emqx_authn/i18n/emqx_authn_api_i18n.conf

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

+ 26 - 0
apps/emqx_authn/src/emqx_authn_api.erl

@@ -257,6 +257,15 @@ 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 => #{
             tags => ?API_TAGS_SINGLE,
             description => ?DESC(listeners_listener_id_authentication_post),
@@ -646,6 +655,13 @@ listener_authenticators(get, #{bindings := #{listener_id := ListenerID}}) ->
         fun(Type, Name, _) ->
             list_authenticators([listeners, Type, Name, authentication])
         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}}) ->
@@ -1098,6 +1114,16 @@ delete_authenticator(ConfKeyPath, ChainName, AuthenticatorID) ->
             serialize_error(Reason)
     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) ->
     case parse_position(Position) of
         {ok, NPosition} ->

+ 78 - 0
apps/emqx_authn/test/emqx_authn_api_SUITE.erl

@@ -693,6 +693,84 @@ test_authenticator_upload_users(PathPrefix) ->
         {filename, "user-credentials.csv", CSVData}
     ]).
 
+t_switch_to_global_chain(_) ->
+    {ok, 200, _} = request(
+        post,
+        uri([?CONF_NS]),
+        emqx_authn_test_lib:built_in_database_example()
+    ),
+
+    {ok, 200, _} = request(
+        post,
+        uri([listeners, "tcp:default", ?CONF_NS]),
+        emqx_authn_test_lib:built_in_database_example()
+    ),
+
+    GlobalUser = #{user_id => <<"global_user">>, password => <<"p1">>},
+
+    {ok, 201, _} = request(
+        post,
+        uri([?CONF_NS, "password_based:built_in_database", "users"]),
+        GlobalUser
+    ),
+
+    ListenerUser = #{user_id => <<"listener_user">>, password => <<"p1">>},
+
+    {ok, 201, _} = request(
+        post,
+        uri([listeners, "tcp:default", ?CONF_NS, "password_based:built_in_database", "users"]),
+        ListenerUser
+    ),
+
+    process_flag(trap_exit, true),
+
+    %% Listener user should be OK
+    {ok, Client0} = emqtt:start_link([
+        {username, <<"listener_user">>},
+        {password, <<"p1">>}
+    ]),
+    ?assertMatch(
+        {ok, _},
+        emqtt:connect(Client0)
+    ),
+    ok = emqtt:disconnect(Client0),
+
+    %% Global user should not be OK
+    {ok, Client1} = emqtt:start_link([
+        {username, <<"global_user">>},
+        {password, <<"p1">>}
+    ]),
+    ?assertMatch(
+        {error, {unauthorized_client, _}},
+        emqtt:connect(Client1)
+    ),
+
+    {ok, 204, _} = request(
+        delete,
+        uri([listeners, "tcp:default", ?CONF_NS])
+    ),
+
+    %% Listener user should not be OK — local chain removed
+    {ok, Client2} = emqtt:start_link([
+        {username, <<"listener_user">>},
+        {password, <<"p1">>}
+    ]),
+    ?assertMatch(
+        {error, {unauthorized_client, _}},
+        emqtt:connect(Client2)
+    ),
+
+    %% Global user should be now OK, switched back to the global chain
+    {ok, Client3} = emqtt:start_link([
+        {username, <<"global_user">>},
+        {password, <<"p1">>}
+    ]),
+    ?assertMatch(
+        {ok, _},
+        emqtt:connect(Client3)
+    ),
+    ok = emqtt:disconnect(Client3).
+
 %%------------------------------------------------------------------------------
 %% Helpers
 %%------------------------------------------------------------------------------

+ 1 - 7
apps/emqx_conf/src/emqx_cluster_rpc.erl

@@ -512,11 +512,5 @@ get_retry_ms() ->
 
 maybe_init_tnx_id(_Node, TnxId) when TnxId < 0 -> ok;
 maybe_init_tnx_id(Node, TnxId) ->
-    {atomic, _} = transaction(fun init_node_tnx_id/2, [Node, TnxId]),
+    {atomic, _} = transaction(fun commit/2, [Node, TnxId]),
     ok.
-
-init_node_tnx_id(Node, TnxId) ->
-    case mnesia:read(?CLUSTER_COMMIT, Node) of
-        [] -> commit(Node, TnxId);
-        _ -> ok
-    end.

+ 4 - 1
apps/emqx_gateway/src/emqx_gateway_api.erl

@@ -352,6 +352,8 @@ fields(Gw) when
 ->
     [{name, mk(Gw, #{desc => ?DESC(gateway_name)})}] ++
         convert_listener_struct(emqx_gateway_schema:fields(Gw));
+fields(update_disable_enable_only) ->
+    [{enable, mk(boolean(), #{desc => <<"Enable/Disable the gateway">>})}];
 fields(Gw) when
     Gw == update_stomp;
     Gw == update_mqttsn;
@@ -411,7 +413,8 @@ schema_update_gateways_conf() ->
             ref(?MODULE, update_mqttsn),
             ref(?MODULE, update_coap),
             ref(?MODULE, update_lwm2m),
-            ref(?MODULE, update_exproto)
+            ref(?MODULE, update_exproto),
+            ref(?MODULE, update_disable_enable_only)
         ]),
         examples_update_gateway_confs()
     ).

+ 33 - 17
apps/emqx_gateway/src/emqx_gateway_conf.erl

@@ -73,6 +73,8 @@
 -type map_or_err() :: {ok, map()} | {error, term()}.
 -type listener_ref() :: {ListenerType :: atom_or_bin(), ListenerName :: atom_or_bin()}.
 
+-define(IS_SSL(T), (T == <<"ssl">> orelse T == <<"dtls">>)).
+
 %%--------------------------------------------------------------------
 %%  Load/Unload
 %%--------------------------------------------------------------------
@@ -403,10 +405,7 @@ pre_config_update(_, {update_gateway, GwName, Conf}, RawConf) ->
         GwRawConf ->
             Conf1 = maps:without([<<"listeners">>, ?AUTHN_BIN], Conf),
             NConf = tune_gw_certs(fun convert_certs/2, GwName, Conf1),
-            NConf1 = maps:merge(
-                maps:with([<<"listeners">>, ?AUTHN_BIN], GwRawConf),
-                NConf
-            ),
+            NConf1 = maps:merge(GwRawConf, NConf),
             {ok, emqx_map_lib:deep_put([GwName], RawConf, NConf1)}
     end;
 pre_config_update(_, {unload_gateway, GwName}, RawConf) ->
@@ -680,9 +679,17 @@ apply_to_listeners(Fun, GwName, Conf) ->
 apply_to_gateway_basic_confs(Fun, <<"exproto">>, Conf) ->
     SvrDir = filename:join(["exproto", "server"]),
     HdrDir = filename:join(["exproto", "handler"]),
-    NServerConf = erlang:apply(Fun, [SvrDir, maps:get(<<"server">>, Conf, #{})]),
-    NHandlerConf = erlang:apply(Fun, [HdrDir, maps:get(<<"handler">>, Conf, #{})]),
-    maps:put(<<"handler">>, NHandlerConf, maps:put(<<"server">>, NServerConf, Conf));
+    Conf1 =
+        case maps:get(<<"server">>, Conf, undefined) of
+            undefined ->
+                Conf;
+            ServerConf ->
+                maps:put(<<"server">>, erlang:apply(Fun, [SvrDir, ServerConf]), Conf)
+        end,
+    case maps:get(<<"handler">>, Conf1, undefined) of
+        undefined -> Conf1;
+        HandlerConf -> maps:put(<<"handler">>, erlang:apply(Fun, [HdrDir, HandlerConf]), Conf1)
+    end;
 apply_to_gateway_basic_confs(_Fun, _GwName, Conf) ->
     Conf.
 
@@ -690,34 +697,43 @@ certs_dir(GwName) when is_binary(GwName) ->
     GwName.
 
 convert_certs(SubDir, Conf) ->
+    convert_certs(<<"dtls">>, SubDir, convert_certs(<<"ssl">>, SubDir, Conf)).
+
+convert_certs(Type, SubDir, Conf) when ?IS_SSL(Type) ->
     case
         emqx_tls_lib:ensure_ssl_files(
             SubDir,
-            maps:get(<<"ssl">>, Conf, undefined)
+            maps:get(Type, Conf, undefined)
         )
     of
         {ok, SSL} ->
-            new_ssl_config(Conf, SSL);
+            new_ssl_config(Type, Conf, SSL);
         {error, Reason} ->
             ?SLOG(error, Reason#{msg => bad_ssl_config}),
             throw({bad_ssl_config, Reason})
-    end.
+    end;
+convert_certs(SubDir, NConf, OConf) when is_map(NConf); is_map(OConf) ->
+    convert_certs(<<"dtls">>, SubDir, convert_certs(<<"ssl">>, SubDir, NConf, OConf), OConf).
 
-convert_certs(SubDir, NConf, OConf) ->
-    OSSL = maps:get(<<"ssl">>, OConf, undefined),
-    NSSL = maps:get(<<"ssl">>, NConf, undefined),
+convert_certs(Type, SubDir, NConf, OConf) when ?IS_SSL(Type) ->
+    OSSL = maps:get(Type, OConf, undefined),
+    NSSL = maps:get(Type, NConf, undefined),
     case emqx_tls_lib:ensure_ssl_files(SubDir, NSSL) of
         {ok, NSSL1} ->
             ok = emqx_tls_lib:delete_ssl_files(SubDir, NSSL1, OSSL),
-            new_ssl_config(NConf, NSSL1);
+            new_ssl_config(Type, NConf, NSSL1);
         {error, Reason} ->
             ?SLOG(error, Reason#{msg => bad_ssl_config}),
             throw({bad_ssl_config, Reason})
     end.
 
-new_ssl_config(Conf, undefined) -> Conf;
-new_ssl_config(Conf, SSL) -> Conf#{<<"ssl">> => SSL}.
+new_ssl_config(_Type, Conf, undefined) -> Conf;
+new_ssl_config(Type, Conf, SSL) when ?IS_SSL(Type) -> Conf#{Type => SSL}.
 
 clear_certs(SubDir, Conf) ->
-    SSL = maps:get(<<"ssl">>, Conf, undefined),
+    clear_certs(<<"ssl">>, SubDir, Conf),
+    clear_certs(<<"dtls">>, SubDir, Conf).
+
+clear_certs(Type, SubDir, Conf) when ?IS_SSL(Type) ->
+    SSL = maps:get(Type, Conf, undefined),
     ok = emqx_tls_lib:delete_ssl_files(SubDir, undefined, SSL).