chore(auth): get rid of hardcoded schema modules in auth

This commit is contained in:
Ilya Averyanov 2023-10-02 15:15:40 +03:00
parent c2c56ba481
commit 5dff36474d
16 changed files with 177 additions and 168 deletions

View File

@ -22,7 +22,11 @@
#{
hookpoint() => [hocon_schema:field()]
}.
-optional_callbacks([injected_fields/0]).
-callback injected_fields(term()) ->
#{
hookpoint() => [hocon_schema:field()]
}.
-optional_callbacks([injected_fields/0, injected_fields/1]).
-export_type([hookpoint/0]).
@ -92,6 +96,8 @@ inject_from_modules(Modules) ->
append_module_injections(Module, AllInjections) when is_atom(Module) ->
append_module_injections(Module:injected_fields(), AllInjections);
append_module_injections({Module, Options}, AllInjections) when is_atom(Module) ->
append_module_injections(Module:injected_fields(Options), AllInjections);
append_module_injections(ModuleInjections, AllInjections) when is_map(ModuleInjections) ->
maps:fold(
fun(PointName, Fields, Acc) ->

View File

@ -1,37 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2023 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.
%%--------------------------------------------------------------------
-ifndef(EMQX_AUTHN_SOURCES_HRL).
-define(EMQX_AUTHN_SOURCES_HRL, true).
-define(PROVIDER_SCHEMA_MODS, [
emqx_authn_mnesia_schema,
emqx_authn_mysql_schema,
emqx_authn_postgresql_schema,
emqx_authn_mongodb_schema,
emqx_authn_redis_schema,
emqx_authn_http_schema,
emqx_authn_jwt_schema,
emqx_authn_scram_mnesia_schema
]).
-define(EE_PROVIDER_SCHEMA_MODS, [
emqx_authn_ldap_schema,
emqx_authn_ldap_bind_schema,
emqx_gcp_device_authn_schema
]).
-endif.

View File

@ -1,34 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2023 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.
%%--------------------------------------------------------------------
-ifndef(EMQX_AUTHZ_SCHEMA_HRL).
-define(EMQX_AUTHZ_SCHEMA_HRL, true).
-define(SOURCE_SCHEMA_MODS, [
emqx_authz_file_schema,
emqx_authz_mnesia_schema,
emqx_authz_http_schema,
emqx_authz_redis_schema,
emqx_authz_mysql_schema,
emqx_authz_postgresql_schema,
emqx_authz_mongodb_schema
]).
-define(EE_SOURCE_SCHEMA_MODS, [
emqx_authz_ldap_schema
]).
-endif.

View File

@ -1,21 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
%%--------------------------------------------------------------------
-module(emqx_authn_enterprise).
-include("emqx_authn_schema.hrl").
-export([provider_schema_mods/0]).
-if(?EMQX_RELEASE_EDITION == ee).
provider_schema_mods() ->
?EE_PROVIDER_SCHEMA_MODS.
-else.
provider_schema_mods() ->
[].
-endif.

View File

@ -19,14 +19,13 @@
-elvis([{elvis_style, invalid_dynamic_call, disable}]).
-include_lib("hocon/include/hoconsc.hrl").
-include("emqx_authn.hrl").
-include("emqx_authn_schema.hrl").
-include("emqx_authn_chains.hrl").
-include_lib("eunit/include/eunit.hrl").
-behaviour(emqx_schema_hooks).
-export([
injected_fields/0
injected_fields/1
]).
-export([
@ -35,7 +34,7 @@
tags/0,
fields/1,
authenticator_type/0,
authenticator_type_without_scram/0,
authenticator_type_without/1,
mechanism/1,
backend/1
]).
@ -44,6 +43,8 @@
global_auth_fields/0
]).
-define(AUTHN_MODS_PT_KEY, {?MODULE, authn_schema_mods}).
%%--------------------------------------------------------------------
%% Authn Source Schema Behaviour
%%--------------------------------------------------------------------
@ -55,7 +56,8 @@
roots() -> [].
injected_fields() ->
injected_fields(AuthnSchemaMods) ->
persistent_term:put(?AUTHN_MODS_PT_KEY, AuthnSchemaMods),
#{
'mqtt.listener' => mqtt_listener_auth_fields(),
'roots.high' => global_auth_fields()
@ -67,9 +69,9 @@ tags() ->
authenticator_type() ->
hoconsc:union(union_member_selector(provider_schema_mods())).
authenticator_type_without_scram() ->
authenticator_type_without(ProviderSchemaMods) ->
hoconsc:union(
union_member_selector(provider_schema_mods() -- [emqx_authn_scram_mnesia_schema])
union_member_selector(provider_schema_mods() -- ProviderSchemaMods)
).
union_member_selector(Mods) ->
@ -241,7 +243,16 @@ validations(Mod) ->
end.
provider_schema_mods() ->
?PROVIDER_SCHEMA_MODS ++ emqx_authn_enterprise:provider_schema_mods().
try
persistent_term:get(?AUTHN_MODS_PT_KEY)
catch
error:badarg ->
%% This may happen only in tests.
%% emqx_conf provides the schema mods for emqx_authn_schema
%% and injects it into the full app's schema.
%% Please check if emqx_conf is properly started.
error(emqx_authn_schema_not_injected)
end.
status() ->
hoconsc:enum([connected, disconnected, connecting]).

View File

@ -1,24 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
%%--------------------------------------------------------------------
-module(emqx_authz_enterprise).
-include("emqx_authz_schema.hrl").
-export([
source_schema_mods/0
]).
-if(?EMQX_RELEASE_EDITION == ee).
source_schema_mods() ->
?EE_SOURCE_SCHEMA_MODS.
-else.
-dialyzer({nowarn_function, [source_schema_mods/0]}).
source_schema_mods() ->
[].
-endif.

View File

@ -17,7 +17,6 @@
-module(emqx_authz_schema).
-include("emqx_authz.hrl").
-include("emqx_authz_schema.hrl").
-include_lib("hocon/include/hoconsc.hrl").
-export([
@ -34,7 +33,7 @@
]).
-export([
injected_fields/0
injected_fields/1
]).
-export([
@ -42,6 +41,8 @@
authz_common_fields/1
]).
-define(AUTHZ_MODS_PT_KEY, {?MODULE, authz_schema_mods}).
%%--------------------------------------------------------------------
%% Authz Source Schema Behaviour
%%--------------------------------------------------------------------
@ -115,7 +116,8 @@ desc(_) ->
%% emqx_schema_hooks behaviour
%%--------------------------------------------------------------------
injected_fields() ->
injected_fields(AuthzSchemaMods) ->
persistent_term:put(?AUTHZ_MODS_PT_KEY, AuthzSchemaMods),
#{
'roots.high' => [
{?CONF_NS, ?HOCON(?R_REF(?CONF_NS), #{desc => ?DESC(?CONF_NS)})}
@ -178,7 +180,16 @@ api_source_refs(Mod) ->
end.
source_schema_mods() ->
?SOURCE_SCHEMA_MODS ++ emqx_authz_enterprise:source_schema_mods().
try
persistent_term:get(?AUTHZ_MODS_PT_KEY)
catch
error:badarg ->
%% This may happen only in tests.
%% emqx_conf provides the schema mods for emqx_authz_schema
%% and injects it into the full app's schema.
%% Please check if emqx_conf is properly started.
error(emqx_authz_schema_not_injected)
end.
common_rate_field() ->
[

View File

@ -40,10 +40,19 @@ end_per_suite(_Config) ->
ok.
init_per_testcase(Case, Config) ->
?MODULE:Case({init, Config}).
Apps = emqx_cth_suite:start(
[
emqx,
emqx_conf,
emqx_auth
],
#{work_dir => emqx_cth_suite:work_dir(Case, Config)}
),
?MODULE:Case({init, [{apps, Apps} | Config]}).
end_per_testcase(Case, Config) ->
?MODULE:Case({'end', Config}).
?MODULE:Case({'end', Config}),
emqx_cth_suite:stop(?config(apps, Config)).
%%=================================================================================
%% Helpers fns
@ -81,7 +90,6 @@ t_fill_defaults(Config) when is_list(Config) ->
).
t_will_message_connection_denied({init, Config}) ->
emqx_common_test_helpers:start_apps([emqx_conf, emqx_auth]),
emqx_authn_test_lib:register_fake_providers([{password_based, built_in_database}]),
AuthnConfig = #{
<<"mechanism">> => <<"password_based">>,
@ -106,7 +114,6 @@ t_will_message_connection_denied({'end', _Config}) ->
[authentication],
{delete_authenticator, 'mqtt:global', <<"password_based:built_in_database">>}
),
emqx_common_test_helpers:stop_apps([emqx_auth, emqx_conf]),
ok;
t_will_message_connection_denied(Config) when is_list(Config) ->
process_flag(trap_exit, true),
@ -146,7 +153,6 @@ t_will_message_connection_denied(Config) when is_list(Config) ->
%% With auth enabled, send CONNECT without password field,
%% expect CONNACK with reason_code=5 and socket close
t_password_undefined({init, Config}) ->
emqx_common_test_helpers:start_apps([emqx_conf, emqx_auth]),
emqx_authn_test_lib:register_fake_providers([
{password_based, built_in_database}
]),
@ -166,7 +172,6 @@ t_password_undefined({'end', _Config}) ->
[authentication],
{delete_authenticator, 'mqtt:global', <<"password_based:built_in_database">>}
),
emqx_common_test_helpers:stop_apps([emqx_auth, emqx_conf]),
ok;
t_password_undefined(Config) when is_list(Config) ->
Payload = <<16, 19, 0, 4, 77, 81, 84, 84, 4, 130, 0, 60, 0, 2, 97, 49, 0, 3, 97, 97, 97>>,
@ -198,7 +203,6 @@ t_password_undefined(Config) when is_list(Config) ->
ok.
t_update_conf({init, Config}) ->
emqx_common_test_helpers:start_apps([emqx_conf, emqx_auth]),
emqx_authn_test_lib:register_fake_providers([
{password_based, built_in_database},
{password_based, http},
@ -208,7 +212,6 @@ t_update_conf({init, Config}) ->
Config;
t_update_conf({'end', _Config}) ->
{ok, _} = emqx:update_config([authentication], []),
emqx_common_test_helpers:stop_apps([emqx_auth, emqx_conf]),
ok;
t_update_conf(Config) when is_list(Config) ->
Authn1 = #{

View File

@ -36,4 +36,49 @@
-define(READONLY_KEYS, [cluster, rpc, node]).
-define(CE_AUTHZ_SOURCE_SCHEMA_MODS, [
emqx_authz_file_schema,
emqx_authz_mnesia_schema,
emqx_authz_http_schema,
emqx_authz_redis_schema,
emqx_authz_mysql_schema,
emqx_authz_postgresql_schema,
emqx_authz_mongodb_schema
]).
-define(EE_AUTHZ_SOURCE_SCHEMA_MODS, [
emqx_authz_ldap_schema
]).
-define(CE_AUTHN_PROVIDER_SCHEMA_MODS, [
emqx_authn_mnesia_schema,
emqx_authn_mysql_schema,
emqx_authn_postgresql_schema,
emqx_authn_mongodb_schema,
emqx_authn_redis_schema,
emqx_authn_http_schema,
emqx_authn_jwt_schema,
emqx_authn_scram_mnesia_schema
]).
-define(EE_AUTHN_PROVIDER_SCHEMA_MODS, [
emqx_authn_ldap_schema,
emqx_authn_ldap_bind_schema,
emqx_gcp_device_authn_schema
]).
-if(?EMQX_RELEASE_EDITION == ee).
-define(AUTHZ_SOURCE_SCHEMA_MODS, ?CE_AUTHZ_SOURCE_SCHEMA_MODS ++ ?EE_AUTHZ_SOURCE_SCHEMA_MODS).
-define(AUTHN_PROVIDER_SCHEMA_MODS,
(?CE_AUTHN_PROVIDER_SCHEMA_MODS ++ ?EE_AUTHN_PROVIDER_SCHEMA_MODS)
).
-else.
-define(AUTHZ_SOURCE_SCHEMA_MODS, ?CE_AUTHZ_SOURCE_SCHEMA_MODS).
-define(AUTHN_PROVIDER_SCHEMA_MODS, ?CE_AUTHN_PROVIDER_SCHEMA_MODS).
-endif.
-endif.

View File

@ -26,6 +26,8 @@
-include_lib("typerefl/include/types.hrl").
-include_lib("hocon/include/hoconsc.hrl").
-include("emqx_conf.hrl").
-type log_level() :: debug | info | notice | warning | error | critical | alert | emergency | all.
-type file() :: string().
-type cipher() :: map().
@ -70,8 +72,8 @@
emqx_mgmt_api_key_schema
]).
-define(INJECTING_CONFIGS, [
emqx_authn_schema,
emqx_authz_schema
{emqx_authn_schema, ?AUTHN_PROVIDER_SCHEMA_MODS},
{emqx_authz_schema, ?AUTHZ_SOURCE_SCHEMA_MODS}
]).
%% 1 million default ports counter

View File

@ -29,6 +29,7 @@
).
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
-include_lib("emqx/include/emqx.hrl").
-include("emqx_dashboard.hrl").
@ -59,11 +60,20 @@ init_per_suite(Config) ->
Apps = emqx_machine_boot:reboot_apps(),
ct:pal("load apps:~p~n", [Apps]),
lists:foreach(fun(App) -> application:load(App) end, Apps),
emqx_mgmt_api_test_util:init_suite([emqx_management]),
Config.
SuiteApps = emqx_cth_suite:start(
[
emqx_conf,
emqx_management,
{emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"}
],
#{work_dir => emqx_cth_suite:work_dir(Config)}
),
emqx_common_test_http:create_default_app(),
[{suite_apps, SuiteApps} | Config].
end_per_suite(_Config) ->
emqx_mgmt_api_test_util:end_suite([emqx_management]).
end_per_suite(Config) ->
emqx_common_test_http:delete_default_app(),
emqx_cth_suite:stop(?config(suite_apps, Config)).
t_overview(_) ->
mnesia:clear_table(?ADMIN),

View File

@ -381,6 +381,6 @@ params_fuzzy_in_qs() ->
schema_authn() ->
emqx_dashboard_swagger:schema_with_examples(
emqx_authn_schema:authenticator_type_without_scram(),
emqx_authn_schema:authenticator_type_without([emqx_authn_scram_mnesia_schema]),
emqx_authn_api:authenticator_examples()
).

View File

@ -31,15 +31,22 @@
all() -> emqx_common_test_helpers:all(?MODULE).
init_per_suite(Conf) ->
emqx_config:erase(gateway),
init_per_suite(Config) ->
emqx_gateway_test_utils:load_all_gateway_apps(),
emqx_common_test_helpers:load_config(emqx_gateway_schema, ?CONF_DEFAULT),
emqx_common_test_helpers:start_apps([emqx_auth, emqx_auth_redis, emqx_auth_mnesia, emqx_gateway]),
Conf.
Apps = emqx_cth_suite:start(
[
{emqx_conf, ?CONF_DEFAULT},
emqx_gateway,
emqx_auth,
emqx_auth_redis,
emqx_auth_mnesia
],
#{work_dir => emqx_cth_suite:work_dir(Config)}
),
[{suite_apps, Apps} | Config].
end_per_suite(_Conf) ->
emqx_common_test_helpers:stop_apps([emqx_gateway, emqx_auth_mnesia, emqx_auth_redis, emqx_auth]),
end_per_suite(Config) ->
emqx_cth_suite:stop(?config(suite_apps, Config)),
emqx_config:delete_override_conf_files(),
ok.

View File

@ -57,13 +57,23 @@ all() -> emqx_common_test_helpers:all(?MODULE).
init_per_suite(Config) ->
application:load(emqx_gateway_coap),
ok = emqx_common_test_helpers:load_config(emqx_gateway_schema, ?CONF_DEFAULT),
emqx_mgmt_api_test_util:init_suite([emqx_conf, emqx_auth, emqx_gateway]),
Config.
Apps = emqx_cth_suite:start(
[
{emqx_conf, ?CONF_DEFAULT},
emqx_gateway,
emqx_auth,
emqx_management,
{emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"}
],
#{work_dir => emqx_cth_suite:work_dir(Config)}
),
emqx_common_test_http:create_default_app(),
[{suite_apps, Apps} | Config].
end_per_suite(_) ->
{ok, _} = emqx:remove_config([<<"gateway">>, <<"coap">>]),
emqx_mgmt_api_test_util:end_suite([emqx_gateway, emqx_auth, emqx_conf]).
end_per_suite(Config) ->
emqx_cth_suite:stop(?config(suite_apps, Config)),
emqx_config:delete_override_conf_files(),
ok.
init_per_testcase(t_connection_with_authn_failed, Config) ->
ok = meck:new(emqx_access_control, [passthrough]),

View File

@ -97,14 +97,23 @@ all() ->
emqx_common_test_helpers:all(?MODULE).
init_per_suite(Config) ->
application:load(emqx_gateway_mqttsn),
ok = emqx_common_test_helpers:load_config(emqx_gateway_schema, ?CONF_DEFAULT),
emqx_mgmt_api_test_util:init_suite([emqx_conf, emqx_auth, emqx_gateway]),
Config.
Apps = emqx_cth_suite:start(
[
{emqx_conf, ?CONF_DEFAULT},
emqx_gateway,
emqx_auth,
emqx_management,
{emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"}
],
#{work_dir => emqx_cth_suite:work_dir(Config)}
),
emqx_common_test_http:create_default_app(),
[{suite_apps, Apps} | Config].
end_per_suite(_) ->
end_per_suite(Config) ->
{ok, _} = emqx:remove_config([gateway, mqttsn]),
emqx_mgmt_api_test_util:end_suite([emqx_gateway, emqx_auth, emqx_conf]).
emqx_common_test_http:delete_default_app(),
emqx_cth_suite:stop(?config(suite_apps, Config)).
restart_mqttsn_with_subs_resume_on() ->
Conf = emqx:get_raw_config([gateway, mqttsn]),

View File

@ -17,6 +17,7 @@
-module(emqx_stomp_SUITE).
-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
-include("emqx_stomp.hrl").
-compile(export_all).
@ -57,14 +58,24 @@ all() -> emqx_common_test_helpers:all(?MODULE).
%% Setups
%%--------------------------------------------------------------------
init_per_suite(Cfg) ->
init_per_suite(Config) ->
application:load(emqx_gateway_stomp),
ok = emqx_common_test_helpers:load_config(emqx_gateway_schema, ?CONF_DEFAULT),
emqx_mgmt_api_test_util:init_suite([emqx_conf, emqx_auth, emqx_gateway]),
Cfg.
Apps = emqx_cth_suite:start(
[
{emqx_conf, ?CONF_DEFAULT},
emqx_gateway,
emqx_auth,
emqx_management,
{emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"}
],
#{work_dir => emqx_cth_suite:work_dir(Config)}
),
emqx_common_test_http:create_default_app(),
[{suite_apps, Apps} | Config].
end_per_suite(_Cfg) ->
emqx_mgmt_api_test_util:end_suite([emqx_gateway, emqx_auth, emqx_conf]),
end_per_suite(Config) ->
emqx_common_test_http:delete_default_app(),
emqx_cth_suite:stop(?config(suite_apps, Config)),
ok.
default_config() ->