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

+ 238 - 0
apps/emqx/test/emqx_authentication_SUITE.erl

@@ -0,0 +1,238 @@
+%%--------------------------------------------------------------------
+%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%--------------------------------------------------------------------
+
+-module(emqx_authentication_SUITE).
+
+-behaviour(hocon_schema).
+-behaviour(emqx_authentication).
+
+-compile(export_all).
+-compile(nowarn_export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+-include_lib("typerefl/include/types.hrl").
+
+-export([ fields/1 ]).
+
+-export([ refs/0
+        , create/1
+        , update/2
+        , authenticate/2
+        , destroy/1
+        ]).
+
+-define(AUTHN, emqx_authentication).
+
+%%------------------------------------------------------------------------------
+%% Hocon Schema
+%%------------------------------------------------------------------------------
+
+fields(type1) ->
+    [ {mechanism,               {enum, ['password-based']}}
+    , {backend,                 {enum, ['built-in-database']}}
+    , {enable,                  fun enable/1}
+    ];
+
+fields(type2) ->
+    [ {mechanism,               {enum, ['password-based']}}
+    , {backend,                 {enum, ['mysql']}}
+    , {enable,                  fun enable/1}
+    ].
+
+enable(type) -> boolean();
+enable(default) -> true;
+enable(_) -> undefined.
+
+%%------------------------------------------------------------------------------
+%% Callbacks
+%%------------------------------------------------------------------------------
+
+refs() ->
+    [ hoconsc:ref(?MODULE, type1)
+    , hoconsc:ref(?MODULE, type2)
+    ].
+
+create(_Config) ->
+    {ok, #{mark => 1}}.
+
+update(_Config, _State) ->
+    {ok, #{mark => 2}}.
+
+authenticate(#{username := <<"good">>}, _State) ->
+    {ok, #{superuser => true}};
+authenticate(#{username := _}, _State) ->
+    {error, bad_username_or_password}.
+
+destroy(_State) ->
+    ok.
+
+all() ->
+    emqx_ct:all(?MODULE).
+
+init_per_suite(Config) ->
+    application:set_env(ekka, strict_mode, true),
+    emqx_ct_helpers:start_apps([]),
+    Config.
+
+end_per_suite(_) ->
+    emqx_ct_helpers:stop_apps([]),
+    ok.
+
+t_chain(_) ->
+    % CRUD of authentication chain
+    ChainName = <<"test">>,
+    ?assertMatch({ok, []}, ?AUTHN:list_chains()),
+    ?assertMatch({ok, #{name := ChainName, authenticators := []}}, ?AUTHN:create_chain(ChainName)),
+    ?assertEqual({error, {already_exists, {chain, ChainName}}}, ?AUTHN:create_chain(ChainName)),
+    ?assertMatch({ok, #{name := ChainName, authenticators := []}}, ?AUTHN:lookup_chain(ChainName)),
+    ?assertMatch({ok, [#{name := ChainName}]}, ?AUTHN:list_chains()),
+    ?assertEqual(ok, ?AUTHN:delete_chain(ChainName)),
+    ?assertMatch({error, {not_found, {chain, ChainName}}}, ?AUTHN:lookup_chain(ChainName)),
+    ok.
+
+t_authenticator(_) ->
+    ChainName = <<"test">>,
+    AuthenticatorConfig1 = #{mechanism => 'password-based',
+                             backend => 'built-in-database',
+                             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
+    ?assertEqual({error, no_available_provider}, ?AUTHN:create_authenticator(ChainName, AuthenticatorConfig1)),
+
+    AuthNType1 = {'password-based', 'built-in-database'},
+    ?AUTHN:add_provider(AuthNType1, ?MODULE),
+    ID1 = <<"password-based:built-in-database">>,
+
+    % CRUD of authencaticator
+    ?assertMatch({ok, #{id := ID1, state := #{mark := 1}}}, ?AUTHN:create_authenticator(ChainName, AuthenticatorConfig1)),
+    ?assertMatch({ok, #{id := ID1}}, ?AUTHN:lookup_authenticator(ChainName, ID1)),
+    ?assertMatch({ok, [#{id := ID1}]}, ?AUTHN:list_authenticators(ChainName)),
+    ?assertEqual({error, {already_exists, {authenticator, ID1}}}, ?AUTHN:create_authenticator(ChainName, AuthenticatorConfig1)),
+    ?assertMatch({ok, #{id := ID1, state := #{mark := 2}}}, ?AUTHN:update_authenticator(ChainName, ID1, AuthenticatorConfig1)),
+    ?assertEqual(ok, ?AUTHN:delete_authenticator(ChainName, ID1)),
+    ?assertEqual({error, {not_found, {authenticator, ID1}}}, ?AUTHN:update_authenticator(ChainName, ID1, AuthenticatorConfig1)),
+    ?assertMatch({ok, []}, ?AUTHN:list_authenticators(ChainName)),
+
+    % Multiple authenticators exist at the same time
+    AuthNType2 = {'password-based', mysql},
+    ?AUTHN:add_provider(AuthNType2, ?MODULE),
+    ID2 = <<"password-based:mysql">>,
+    AuthenticatorConfig2 = #{mechanism => 'password-based',
+                             backend => mysql,
+                             enable => true},
+    ?assertMatch({ok, #{id := ID1}}, ?AUTHN:create_authenticator(ChainName, AuthenticatorConfig1)),
+    ?assertMatch({ok, #{id := ID2}}, ?AUTHN:create_authenticator(ChainName, AuthenticatorConfig2)),
+
+    % Move authenticator
+    ?assertMatch({ok, [#{id := ID1}, #{id := ID2}]}, ?AUTHN:list_authenticators(ChainName)),
+    ?assertEqual(ok, ?AUTHN:move_authenticator(ChainName, ID2, top)),
+    ?assertMatch({ok, [#{id := ID2}, #{id := ID1}]}, ?AUTHN:list_authenticators(ChainName)),
+    ?assertEqual(ok, ?AUTHN:move_authenticator(ChainName, ID2, bottom)),
+    ?assertMatch({ok, [#{id := ID1}, #{id := ID2}]}, ?AUTHN:list_authenticators(ChainName)),
+    ?assertEqual(ok, ?AUTHN:move_authenticator(ChainName, ID2, {before, ID1})),
+    ?assertMatch({ok, [#{id := ID2}, #{id := ID1}]}, ?AUTHN:list_authenticators(ChainName)),
+
+    ?AUTHN:delete_chain(ChainName),
+    ?AUTHN:remove_provider(AuthNType1),
+    ?AUTHN:remove_provider(AuthNType2),
+    ok.
+
+t_authenticate(_) ->
+    ListenerID = <<"tcp:default">>,
+    ClientInfo = #{zone => default,
+                   listener => ListenerID,
+                   protocol => mqtt,
+                   username => <<"good">>,
+			       password => <<"any">>},
+    ?assertEqual({ok, #{superuser => false}}, emqx_access_control:authenticate(ClientInfo)),
+
+    AuthNType = {'password-based', 'built-in-database'},
+    ?AUTHN:add_provider(AuthNType, ?MODULE),
+
+    AuthenticatorConfig = #{mechanism => 'password-based',
+                            backend => 'built-in-database',
+                            enable => true},
+    ?AUTHN:create_chain(ListenerID),
+    ?assertMatch({ok, _}, ?AUTHN:create_authenticator(ListenerID, AuthenticatorConfig)),
+    ?assertEqual({ok, #{superuser => true}}, emqx_access_control:authenticate(ClientInfo)),
+    ?assertEqual({error, bad_username_or_password}, emqx_access_control:authenticate(ClientInfo#{username => <<"bad">>})),
+
+    ?AUTHN:delete_chain(ListenerID),
+    ?AUTHN:remove_provider(AuthNType),
+    ok.
+
+t_update_config(_) ->
+    emqx_config_handler:add_handler([authentication], emqx_authentication),
+
+    AuthNType1 = {'password-based', 'built-in-database'},
+    AuthNType2 = {'password-based', mysql},
+    ?AUTHN:add_provider(AuthNType1, ?MODULE),
+    ?AUTHN:add_provider(AuthNType2, ?MODULE),
+
+    Global = <<"mqtt:global">>,
+    AuthenticatorConfig1 = #{mechanism => 'password-based',
+                             backend => 'built-in-database',
+                             enable => true},
+    AuthenticatorConfig2 = #{mechanism => 'password-based',
+                             backend => mysql,
+                             enable => true},
+    ID1 = <<"password-based:built-in-database">>,
+    ID2 = <<"password-based:mysql">>,
+    
+    ?assertMatch({ok, []}, ?AUTHN:list_chains()),
+    ?assertMatch({ok, _}, update_config([authentication], {create_authenticator, Global, AuthenticatorConfig1})),
+    ?assertMatch({ok, #{id := ID1, state := #{mark := 1}}}, ?AUTHN:lookup_authenticator(Global, ID1)),
+
+    ?assertMatch({ok, _}, update_config([authentication], {create_authenticator, Global, AuthenticatorConfig2})),
+    ?assertMatch({ok, #{id := ID2, state := #{mark := 1}}}, ?AUTHN:lookup_authenticator(Global, ID2)),
+
+    ?assertMatch({ok, _}, update_config([authentication], {update_authenticator, Global, ID1, #{}})),
+    ?assertMatch({ok, #{id := ID1, state := #{mark := 2}}}, ?AUTHN:lookup_authenticator(Global, ID1)),
+
+    ?assertMatch({ok, _}, update_config([authentication], {move_authenticator, Global, ID2, <<"top">>})),
+    ?assertMatch({ok, [#{id := ID2}, #{id := ID1}]}, ?AUTHN:list_authenticators(Global)),
+
+    ?assertMatch({ok, _}, update_config([authentication], {delete_authenticator, Global, ID1})),
+    ?assertEqual({error, {not_found, {authenticator, ID1}}}, ?AUTHN:lookup_authenticator(Global, ID1)),
+
+    ListenerID = <<"tcp:default">>,
+    ConfKeyPath = [listeners, tcp, default, authentication],
+    ?assertMatch({ok, _}, update_config(ConfKeyPath, {create_authenticator, ListenerID, AuthenticatorConfig1})),
+    ?assertMatch({ok, #{id := ID1, state := #{mark := 1}}}, ?AUTHN:lookup_authenticator(ListenerID, ID1)),
+
+    ?assertMatch({ok, _}, update_config(ConfKeyPath, {create_authenticator, ListenerID, AuthenticatorConfig2})),
+    ?assertMatch({ok, #{id := ID2, state := #{mark := 1}}}, ?AUTHN:lookup_authenticator(ListenerID, ID2)),
+
+    ?assertMatch({ok, _}, update_config(ConfKeyPath, {update_authenticator, ListenerID, ID1, #{}})),
+    ?assertMatch({ok, #{id := ID1, state := #{mark := 2}}}, ?AUTHN:lookup_authenticator(ListenerID, ID1)),
+
+    ?assertMatch({ok, _}, update_config(ConfKeyPath, {move_authenticator, ListenerID, ID2, <<"top">>})),
+    ?assertMatch({ok, [#{id := ID2}, #{id := ID1}]}, ?AUTHN:list_authenticators(ListenerID)),
+
+    ?assertMatch({ok, _}, update_config(ConfKeyPath, {delete_authenticator, ListenerID, ID1})),
+    ?assertEqual({error, {not_found, {authenticator, ID1}}}, ?AUTHN:lookup_authenticator(ListenerID, ID1)),
+
+    ?AUTHN:delete_chain(Global),
+    ?AUTHN:remove_provider(AuthNType1),
+    ?AUTHN:remove_provider(AuthNType2),
+    ok.
+
+update_config(Path, ConfigRequest) ->
+    emqx:update_config(Path, ConfigRequest, #{rawconf_with_defaults => true}).

+ 2 - 2
apps/emqx_authn/src/simple_authn/emqx_authn_http.erl

@@ -251,8 +251,8 @@ generate_request(Credential, #{method := Method,
         post ->
             NPath = append_query(Path, BaseQuery),
             ContentType = proplists:get_value(<<"content-type">>, Headers),
-            Body = serialize_body(ContentType, Body),
-            {NPath, Headers, Body}
+            NBody = serialize_body(ContentType, Body),
+            {NPath, Headers, NBody}
     end.
 
 replace_placeholders(KVs, Credential) ->

+ 5 - 0
apps/emqx_authn/test/emqx_authn_SUITE.erl

@@ -15,3 +15,8 @@
 %%--------------------------------------------------------------------
 
 -module(emqx_authn_SUITE).
+
+-compile(export_all).
+-compile(nowarn_export_all).
+
+all() -> emqx_ct:all(?MODULE).

+ 4 - 4
apps/emqx_authn/test/emqx_authn_jwt_SUITE.erl

@@ -16,8 +16,8 @@
 
 -module(emqx_authn_jwt_SUITE).
 
-% -compile(export_all).
-% -compile(nowarn_export_all).
+-compile(export_all).
+-compile(nowarn_export_all).
 
 % -include_lib("common_test/include/ct.hrl").
 % -include_lib("eunit/include/eunit.hrl").
@@ -26,8 +26,8 @@
 
 % -define(AUTH, emqx_authn).
 
-% all() ->
-%     emqx_ct:all(?MODULE).
+all() ->
+    emqx_ct:all(?MODULE).
 
 % init_per_suite(Config) ->
 %     emqx_ct_helpers:start_apps([emqx_authn]),

+ 4 - 4
apps/emqx_authn/test/emqx_authn_mnesia_SUITE.erl

@@ -16,8 +16,8 @@
 
 -module(emqx_authn_mnesia_SUITE).
 
-% -compile(export_all).
-% -compile(nowarn_export_all).
+-compile(export_all).
+-compile(nowarn_export_all).
 
 % -include_lib("common_test/include/ct.hrl").
 % -include_lib("eunit/include/eunit.hrl").
@@ -26,8 +26,8 @@
 
 % -define(AUTH, emqx_authn).
 
-% all() ->
-%     emqx_ct:all(?MODULE).
+all() ->
+    emqx_ct:all(?MODULE).
 
 % init_per_suite(Config) ->
 %     emqx_ct_helpers:start_apps([emqx_authn]),

+ 3 - 1
apps/emqx_connector/src/emqx_connector_schema_lib.erl

@@ -142,7 +142,9 @@ to_ip_port(Str) ->
          _ -> {error, Str}
      end.
 
-ip_port_to_string({Ip, Port}) ->
+ip_port_to_string({Ip, Port}) when is_list(Ip) ->
+    iolist_to_binary([Ip, ":", integer_to_list(Port)]);
+ip_port_to_string({Ip, Port}) when is_tuple(Ip) ->
     iolist_to_binary([inet:ntoa(Ip), ":", integer_to_list(Port)]).
 
 to_servers(Str) ->

+ 1 - 1
apps/emqx_gateway/src/emqx_gateway.app.src

@@ -3,7 +3,7 @@
   {vsn, "0.1.0"},
   {registered, []},
   {mod, {emqx_gateway_app, []}},
-  {applications, [kernel, stdlib, grpc, lwm2m_coap, emqx, emqx_authn]},
+  {applications, [kernel, stdlib, grpc, lwm2m_coap, emqx]},
   {env, []},
   {modules, []},
   {licenses, ["Apache 2.0"]},

+ 3 - 4
apps/emqx_gateway/test/emqx_exproto_SUITE.erl

@@ -55,20 +55,19 @@ metrics() ->
 init_per_group(GrpName, Cfg) ->
     put(grpname, GrpName),
     Svrs = emqx_exproto_echo_svr:start(),
-    emqx_ct_helpers:start_apps([emqx_authn, emqx_gateway], fun set_special_cfg/1),
+    emqx_ct_helpers:start_apps([emqx_gateway], fun set_special_cfg/1),
     emqx_logger:set_log_level(debug),
     [{servers, Svrs}, {listener_type, GrpName} | Cfg].
 
 end_per_group(_, Cfg) ->
-    emqx_ct_helpers:stop_apps([emqx_gateway, emqx_authn]),
+    emqx_ct_helpers:stop_apps([emqx_gateway]),
     emqx_exproto_echo_svr:stop(proplists:get_value(servers, Cfg)).
 
 set_special_cfg(emqx_gateway) ->
     LisType = get(grpname),
     emqx_config:put(
       [gateway, exproto],
-      #{authentication => #{enable => false},
-        server => #{bind => 9100},
+      #{server => #{bind => 9100},
         handler => #{address => "http://127.0.0.1:9001"},
         listeners => listener_confs(LisType)
        });

+ 2 - 2
apps/emqx_gateway/test/emqx_gateway_registry_SUITE.erl

@@ -35,11 +35,11 @@ all() -> emqx_ct:all(?MODULE).
 
 init_per_suite(Cfg) ->
     ok = emqx_config:init_load(emqx_gateway_schema, ?CONF_DEFAULT),
-    emqx_ct_helpers:start_apps([emqx_authn, emqx_gateway]),
+    emqx_ct_helpers:start_apps([emqx_gateway]),
     Cfg.
 
 end_per_suite(_Cfg) ->
-    emqx_ct_helpers:stop_apps([emqx_authn, emqx_gateway]),
+    emqx_ct_helpers:stop_apps([emqx_gateway]),
     ok.
 
 %%--------------------------------------------------------------------

+ 2 - 2
apps/emqx_gateway/test/emqx_lwm2m_SUITE.erl

@@ -148,12 +148,12 @@ groups() ->
     ].
 
 init_per_suite(Config) ->
-    emqx_ct_helpers:start_apps([emqx_authn]),
+    emqx_ct_helpers:start_apps([]),
     Config.
 
 end_per_suite(Config) ->
     timer:sleep(300),
-    emqx_ct_helpers:stop_apps([emqx_authn]),
+    emqx_ct_helpers:stop_apps([]),
     Config.
 
 init_per_testcase(_AllTestCase, Config) ->

+ 2 - 2
apps/emqx_gateway/test/emqx_sn_protocol_SUITE.erl

@@ -83,11 +83,11 @@ all() ->
 
 init_per_suite(Config) ->
     ok = emqx_config:init_load(emqx_gateway_schema, ?CONF_DEFAULT),
-    emqx_ct_helpers:start_apps([emqx_authn, emqx_gateway]),
+    emqx_ct_helpers:start_apps([emqx_gateway]),
     Config.
 
 end_per_suite(_) ->
-    emqx_ct_helpers:stop_apps([emqx_gateway, emqx_authn]).
+    emqx_ct_helpers:stop_apps([emqx_gateway]).
 
 %%--------------------------------------------------------------------
 %% Test cases

+ 2 - 2
apps/emqx_gateway/test/emqx_stomp_SUITE.erl

@@ -43,11 +43,11 @@ all() -> emqx_ct:all(?MODULE).
 
 init_per_suite(Cfg) ->
     ok = emqx_config:init_load(emqx_gateway_schema, ?CONF_DEFAULT),
-    emqx_ct_helpers:start_apps([emqx_authn, emqx_gateway]),
+    emqx_ct_helpers:start_apps([emqx_gateway]),
     Cfg.
 
 end_per_suite(_Cfg) ->
-    emqx_ct_helpers:stop_apps([emqx_gateway, emqx_authn]),
+    emqx_ct_helpers:stop_apps([emqx_gateway]),
     ok.
 
 %%--------------------------------------------------------------------