From aae2d01582b980302f30913522eedf1d9d3d217c Mon Sep 17 00:00:00 2001 From: JimMoen Date: Fri, 1 Apr 2022 02:09:42 +0800 Subject: [PATCH] style: erlfmt apps/emqx_authn --- apps/emqx_authn/rebar.config | 37 +- apps/emqx_authn/src/emqx_authn.app.src | 24 +- apps/emqx_authn/src/emqx_authn.erl | 49 +- apps/emqx_authn/src/emqx_authn_api.erl | 951 +++++++++++------- apps/emqx_authn/src/emqx_authn_app.erl | 38 +- .../src/emqx_authn_password_hashing.erl | 150 +-- apps/emqx_authn/src/emqx_authn_schema.erl | 95 +- apps/emqx_authn/src/emqx_authn_sup.erl | 7 +- apps/emqx_authn/src/emqx_authn_utils.erl | 82 +- apps/emqx_authn/test/emqx_authn_api_SUITE.erl | 439 ++++---- .../emqx_authn/test/emqx_authn_http_SUITE.erl | 483 +++++---- .../test/emqx_authn_http_test_server.erl | 51 +- .../test/emqx_authn_https_SUITE.erl | 109 +- apps/emqx_authn/test/emqx_authn_jwt_SUITE.erl | 225 +++-- .../test/emqx_authn_mnesia_SUITE.erl | 146 +-- .../test/emqx_authn_mongo_SUITE.erl | 486 ++++----- .../test/emqx_authn_mongo_tls_SUITE.erl | 175 ++-- .../test/emqx_authn_mqtt_test_client.erl | 64 +- .../test/emqx_authn_mysql_SUITE.erl | 551 +++++----- .../test/emqx_authn_mysql_tls_SUITE.erl | 107 +- .../emqx_authn_password_hashing_SUITE.erl | 208 ++-- .../test/emqx_authn_pgsql_SUITE.erl | 584 ++++++----- .../test/emqx_authn_pgsql_tls_SUITE.erl | 102 +- .../test/emqx_authn_redis_SUITE.erl | 550 +++++----- .../test/emqx_authn_redis_tls_SUITE.erl | 99 +- apps/emqx_authn/test/emqx_authn_test_lib.erl | 20 +- ...emqx_enhanced_authn_scram_mnesia_SUITE.erl | 274 ++--- 27 files changed, 3509 insertions(+), 2597 deletions(-) diff --git a/apps/emqx_authn/rebar.config b/apps/emqx_authn/rebar.config index d17f1fb90..8fd9cea0f 100644 --- a/apps/emqx_authn/rebar.config +++ b/apps/emqx_authn/rebar.config @@ -1,23 +1,32 @@ %% -*- mode: erlang -*- -{deps, - [ {emqx, {path, "../emqx"}} - , {emqx_connector, {path, "../emqx_connector"}} - ]}. +{deps, [ + {emqx, {path, "../emqx"}}, + {emqx_connector, {path, "../emqx_connector"}} +]}. {edoc_opts, [{preprocess, true}]}. -{erl_opts, [warn_unused_vars, - warn_shadow_vars, - warnings_as_errors, - warn_unused_import, - warn_obsolete_guard, - debug_info, - {parse_transform}]}. +{erl_opts, [ + warn_unused_vars, + warn_shadow_vars, + warnings_as_errors, + warn_unused_import, + warn_obsolete_guard, + debug_info, + {parse_transform} +]}. -{xref_checks, [undefined_function_calls, undefined_functions, - locals_not_used, deprecated_function_calls, - warnings_as_errors, deprecated_functions]}. +{xref_checks, [ + undefined_function_calls, + undefined_functions, + locals_not_used, + deprecated_function_calls, + warnings_as_errors, + deprecated_functions +]}. {cover_enabled, true}. {cover_opts, [verbose]}. {cover_export_enabled, true}. + +{project_plugins, [erlfmt]}. diff --git a/apps/emqx_authn/src/emqx_authn.app.src b/apps/emqx_authn/src/emqx_authn.app.src index 799478be8..d32b74e49 100644 --- a/apps/emqx_authn/src/emqx_authn.app.src +++ b/apps/emqx_authn/src/emqx_authn.app.src @@ -1,13 +1,13 @@ %% -*- mode: erlang -*- -{application, emqx_authn, - [{description, "EMQX Authentication"}, - {vsn, "0.1.0"}, - {modules, []}, - {registered, [emqx_authn_sup, emqx_authn_registry]}, - {applications, [kernel,stdlib,emqx_resource,ehttpc,epgsql,mysql,jose]}, - {mod, {emqx_authn_app,[]}}, - {env, []}, - {licenses, ["Apache-2.0"]}, - {maintainers, ["EMQX Team "]}, - {links, [{"Homepage", "https://emqx.io/"}]} - ]}. +{application, emqx_authn, [ + {description, "EMQX Authentication"}, + {vsn, "0.1.0"}, + {modules, []}, + {registered, [emqx_authn_sup, emqx_authn_registry]}, + {applications, [kernel, stdlib, emqx_resource, ehttpc, epgsql, mysql, jose]}, + {mod, {emqx_authn_app, []}}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQX Team "]}, + {links, [{"Homepage", "https://emqx.io/"}]} +]}. diff --git a/apps/emqx_authn/src/emqx_authn.erl b/apps/emqx_authn/src/emqx_authn.erl index f984c06ec..f93212bcf 100644 --- a/apps/emqx_authn/src/emqx_authn.erl +++ b/apps/emqx_authn/src/emqx_authn.erl @@ -16,28 +16,31 @@ -module(emqx_authn). --export([ providers/0 - , check_config/1 - , check_config/2 - , check_configs/1 - ]). +-export([ + providers/0, + check_config/1, + check_config/2, + check_configs/1 +]). -include("emqx_authn.hrl"). providers() -> - [ {{'password_based', 'built_in_database'}, emqx_authn_mnesia} - , {{'password_based', mysql}, emqx_authn_mysql} - , {{'password_based', postgresql}, emqx_authn_pgsql} - , {{'password_based', mongodb}, emqx_authn_mongodb} - , {{'password_based', redis}, emqx_authn_redis} - , {{'password_based', 'http'}, emqx_authn_http} - , {jwt, emqx_authn_jwt} - , {{scram, 'built_in_database'}, emqx_enhanced_authn_scram_mnesia} + [ + {{'password_based', 'built_in_database'}, emqx_authn_mnesia}, + {{'password_based', mysql}, emqx_authn_mysql}, + {{'password_based', postgresql}, emqx_authn_pgsql}, + {{'password_based', mongodb}, emqx_authn_mongodb}, + {{'password_based', redis}, emqx_authn_redis}, + {{'password_based', 'http'}, emqx_authn_http}, + {jwt, emqx_authn_jwt}, + {{scram, 'built_in_database'}, emqx_enhanced_authn_scram_mnesia} ]. check_configs(C) when is_map(C) -> check_configs([C]); -check_configs([]) -> []; +check_configs([]) -> + []; check_configs([Config | Configs]) -> [check_config(Config) | check_configs(Configs)]. @@ -51,22 +54,26 @@ check_config(Config, Opts) -> end. do_check_config(#{<<"mechanism">> := Mec} = Config, Opts) -> - Key = case maps:get(<<"backend">>, Config, false) of - false -> atom(Mec); - Backend -> {atom(Mec), atom(Backend)} - end, + Key = + case maps:get(<<"backend">>, Config, false) of + false -> atom(Mec); + Backend -> {atom(Mec), atom(Backend)} + end, case lists:keyfind(Key, 1, providers()) of false -> throw({unknown_handler, Key}); {_, ProviderModule} -> - hocon_tconf:check_plain(ProviderModule, #{?CONF_NS_BINARY => Config}, - Opts#{atom_key => true}) + hocon_tconf:check_plain( + ProviderModule, + #{?CONF_NS_BINARY => Config}, + Opts#{atom_key => true} + ) end. atom(Bin) -> try binary_to_existing_atom(Bin, utf8) catch - _ : _ -> + _:_ -> throw({unknown_auth_provider, Bin}) end. diff --git a/apps/emqx_authn/src/emqx_authn_api.erl b/apps/emqx_authn/src/emqx_authn_api.erl index 35352dc2a..927fe4072 100644 --- a/apps/emqx_authn/src/emqx_authn_api.erl +++ b/apps/emqx_authn/src/emqx_authn_api.erl @@ -33,113 +33,120 @@ % Swagger --define(API_TAGS_GLOBAL, [?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY, - <<"authentication config(global)">>]). --define(API_TAGS_SINGLE, [?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY, - <<"authentication config(single listener)">>]). +-define(API_TAGS_GLOBAL, [ + ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY, + <<"authentication config(global)">> +]). +-define(API_TAGS_SINGLE, [ + ?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY, + <<"authentication config(single listener)">> +]). --export([ api_spec/0 - , paths/0 - , schema/1 - ]). +-export([ + api_spec/0, + paths/0, + schema/1 +]). --export([ roots/0 - , fields/1 - ]). +-export([ + roots/0, + fields/1 +]). --export([ authenticators/2 - , authenticator/2 - , authenticator_status/2 - , listener_authenticators/2 - , listener_authenticator/2 - , listener_authenticator_status/2 - , authenticator_move/2 - , listener_authenticator_move/2 - , authenticator_import_users/2 - , listener_authenticator_import_users/2 - , authenticator_users/2 - , authenticator_user/2 - , listener_authenticator_users/2 - , listener_authenticator_user/2 - , lookup_from_local_node/2 - , lookup_from_all_nodes/2 - ]). +-export([ + authenticators/2, + authenticator/2, + authenticator_status/2, + listener_authenticators/2, + listener_authenticator/2, + listener_authenticator_status/2, + authenticator_move/2, + listener_authenticator_move/2, + authenticator_import_users/2, + listener_authenticator_import_users/2, + authenticator_users/2, + authenticator_user/2, + listener_authenticator_users/2, + listener_authenticator_user/2, + lookup_from_local_node/2, + lookup_from_all_nodes/2 +]). --export([ authenticator_examples/0 - , request_move_examples/0 - , request_import_users_examples/0 - , request_user_create_examples/0 - , request_user_update_examples/0 - , response_user_examples/0 - , response_users_example/0 - ]). +-export([ + authenticator_examples/0, + request_move_examples/0, + request_import_users_examples/0, + request_user_create_examples/0, + request_user_update_examples/0, + response_user_examples/0, + response_users_example/0 +]). %% export these funcs for gateway --export([ list_users/3 - , add_user/3 - , delete_user/3 - , find_user/3 - , update_user/4 - , serialize_error/1 - ]). +-export([ + list_users/3, + add_user/3, + delete_user/3, + find_user/3, + update_user/4, + serialize_error/1 +]). -elvis([{elvis_style, god_modules, disable}]). api_spec() -> emqx_dashboard_swagger:spec(?MODULE, #{check_schema => true}). -paths() -> [ "/authentication" - , "/authentication/:id" - , "/authentication/:id/status" - , "/authentication/:id/move" - , "/authentication/:id/import_users" - , "/authentication/:id/users" - , "/authentication/:id/users/:user_id" +paths() -> + [ + "/authentication", + "/authentication/:id", + "/authentication/:id/status", + "/authentication/:id/move", + "/authentication/:id/import_users", + "/authentication/:id/users", + "/authentication/:id/users/:user_id", - , "/listeners/:listener_id/authentication" - , "/listeners/:listener_id/authentication/:id" - , "/listeners/:listener_id/authentication/:id/status" - , "/listeners/:listener_id/authentication/:id/move" - , "/listeners/:listener_id/authentication/:id/import_users" - , "/listeners/:listener_id/authentication/:id/users" - , "/listeners/:listener_id/authentication/:id/users/:user_id" - ]. + "/listeners/:listener_id/authentication", + "/listeners/:listener_id/authentication/:id", + "/listeners/:listener_id/authentication/:id/status", + "/listeners/:listener_id/authentication/:id/move", + "/listeners/:listener_id/authentication/:id/import_users", + "/listeners/:listener_id/authentication/:id/users", + "/listeners/:listener_id/authentication/:id/users/:user_id" + ]. -roots() -> [ request_user_create - , request_user_update - , request_move - , request_import_users - , response_user - , response_users - ]. +roots() -> + [ + request_user_create, + request_user_update, + request_move, + request_import_users, + response_user, + response_users + ]. fields(request_user_create) -> [ {user_id, binary()} | fields(request_user_update) ]; - fields(request_user_update) -> [ {password, binary()}, {is_superuser, mk(boolean(), #{default => false, required => false})} ]; - fields(request_move) -> [{position, binary()}]; - fields(request_import_users) -> [{filename, binary()}]; - fields(response_user) -> [ {user_id, binary()}, {is_superuser, mk(boolean(), #{default => false, required => false})} ]; - fields(response_users) -> paginated_list_type(ref(response_user)); - fields(pagination_meta) -> [ {page, non_neg_integer()}, @@ -156,7 +163,8 @@ schema("/authentication") -> responses => #{ 200 => emqx_dashboard_swagger:schema_with_example( hoconsc:array(emqx_authn_schema:authenticator_type()), - authenticator_array_example()) + authenticator_array_example() + ) } }, post => #{ @@ -164,17 +172,18 @@ schema("/authentication") -> description => <<"Create authenticator for global authentication">>, 'requestBody' => emqx_dashboard_swagger:schema_with_examples( emqx_authn_schema:authenticator_type(), - authenticator_examples()), + authenticator_examples() + ), responses => #{ 200 => emqx_dashboard_swagger:schema_with_examples( emqx_authn_schema:authenticator_type(), - authenticator_examples()), + authenticator_examples() + ), 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), 409 => error_codes([?ALREADY_EXISTS], <<"ALREADY_EXISTS">>) } } }; - schema("/authentication/:id") -> #{ 'operationId' => authenticator, @@ -185,7 +194,8 @@ schema("/authentication/:id") -> responses => #{ 200 => emqx_dashboard_swagger:schema_with_examples( emqx_authn_schema:authenticator_type(), - authenticator_examples()), + authenticator_examples() + ), 404 => error_codes([?NOT_FOUND], <<"Not Found">>) } }, @@ -200,7 +210,8 @@ schema("/authentication/:id") -> responses => #{ 200 => emqx_dashboard_swagger:schema_with_examples( emqx_authn_schema:authenticator_type(), - authenticator_examples()), + authenticator_examples() + ), 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), 404 => error_codes([?NOT_FOUND], <<"Not Found">>), 409 => error_codes([?ALREADY_EXISTS], <<"ALREADY_EXISTS">>) @@ -216,7 +227,6 @@ schema("/authentication/:id") -> } } }; - schema("/authentication/:id/status") -> #{ 'operationId' => authenticator_status, @@ -227,12 +237,12 @@ schema("/authentication/:id/status") -> responses => #{ 200 => emqx_dashboard_swagger:schema_with_examples( hoconsc:ref(emqx_authn_schema, "metrics_status_fields"), - status_metrics_example()), + status_metrics_example() + ), 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) } } }; - schema("/listeners/:listener_id/authentication") -> #{ 'operationId' => listener_authenticators, @@ -243,7 +253,8 @@ schema("/listeners/:listener_id/authentication") -> responses => #{ 200 => emqx_dashboard_swagger:schema_with_example( hoconsc:array(emqx_authn_schema:authenticator_type()), - authenticator_array_example()) + authenticator_array_example() + ) } }, post => #{ @@ -257,13 +268,13 @@ schema("/listeners/:listener_id/authentication") -> responses => #{ 200 => emqx_dashboard_swagger:schema_with_examples( emqx_authn_schema:authenticator_type(), - authenticator_examples()), + authenticator_examples() + ), 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), 409 => error_codes([?ALREADY_EXISTS], <<"ALREADY_EXISTS">>) } } }; - schema("/listeners/:listener_id/authentication/:id") -> #{ 'operationId' => listener_authenticator, @@ -274,7 +285,8 @@ schema("/listeners/:listener_id/authentication/:id") -> responses => #{ 200 => emqx_dashboard_swagger:schema_with_examples( emqx_authn_schema:authenticator_type(), - authenticator_examples()), + authenticator_examples() + ), 404 => error_codes([?NOT_FOUND], <<"Not Found">>) } }, @@ -284,11 +296,13 @@ schema("/listeners/:listener_id/authentication/:id") -> parameters => [param_listener_id(), param_auth_id()], 'requestBody' => emqx_dashboard_swagger:schema_with_examples( emqx_authn_schema:authenticator_type(), - authenticator_examples()), + authenticator_examples() + ), responses => #{ 200 => emqx_dashboard_swagger:schema_with_examples( emqx_authn_schema:authenticator_type(), - authenticator_examples()), + authenticator_examples() + ), 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), 404 => error_codes([?NOT_FOUND], <<"Not Found">>), 409 => error_codes([?ALREADY_EXISTS], <<"ALREADY_EXISTS">>) @@ -304,7 +318,6 @@ schema("/listeners/:listener_id/authentication/:id") -> } } }; - schema("/listeners/:listener_id/authentication/:id/status") -> #{ 'operationId' => listener_authenticator_status, @@ -315,12 +328,12 @@ schema("/listeners/:listener_id/authentication/:id/status") -> responses => #{ 200 => emqx_dashboard_swagger:schema_with_examples( hoconsc:ref(emqx_authn_schema, "metrics_status_fields"), - status_metrics_example()), + status_metrics_example() + ), 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>) } } }; - schema("/authentication/:id/move") -> #{ 'operationId' => authenticator_move, @@ -330,7 +343,8 @@ schema("/authentication/:id/move") -> parameters => [param_auth_id()], 'requestBody' => emqx_dashboard_swagger:schema_with_examples( ref(request_move), - request_move_examples()), + request_move_examples() + ), responses => #{ 204 => <<"Authenticator moved">>, 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), @@ -338,7 +352,6 @@ schema("/authentication/:id/move") -> } } }; - schema("/listeners/:listener_id/authentication/:id/move") -> #{ 'operationId' => listener_authenticator_move, @@ -348,7 +361,8 @@ schema("/listeners/:listener_id/authentication/:id/move") -> parameters => [param_listener_id(), param_auth_id()], 'requestBody' => emqx_dashboard_swagger:schema_with_examples( ref(request_move), - request_move_examples()), + request_move_examples() + ), responses => #{ 204 => <<"Authenticator moved">>, 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), @@ -356,7 +370,6 @@ schema("/listeners/:listener_id/authentication/:id/move") -> } } }; - schema("/authentication/:id/import_users") -> #{ 'operationId' => authenticator_import_users, @@ -366,7 +379,8 @@ schema("/authentication/:id/import_users") -> parameters => [param_auth_id()], 'requestBody' => emqx_dashboard_swagger:schema_with_examples( ref(request_import_users), - request_import_users_examples()), + request_import_users_examples() + ), responses => #{ 204 => <<"Users imported">>, 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), @@ -374,7 +388,6 @@ schema("/authentication/:id/import_users") -> } } }; - schema("/listeners/:listener_id/authentication/:id/import_users") -> #{ 'operationId' => listener_authenticator_import_users, @@ -384,7 +397,8 @@ schema("/listeners/:listener_id/authentication/:id/import_users") -> parameters => [param_listener_id(), param_auth_id()], 'requestBody' => emqx_dashboard_swagger:schema_with_examples( ref(request_import_users), - request_import_users_examples()), + request_import_users_examples() + ), responses => #{ 204 => <<"Users imported">>, 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), @@ -392,7 +406,6 @@ schema("/listeners/:listener_id/authentication/:id/import_users") -> } } }; - schema("/authentication/:id/users") -> #{ 'operationId' => authenticator_users, @@ -402,11 +415,13 @@ schema("/authentication/:id/users") -> parameters => [param_auth_id()], 'requestBody' => emqx_dashboard_swagger:schema_with_examples( ref(request_user_create), - request_user_create_examples()), + request_user_create_examples() + ), responses => #{ 201 => emqx_dashboard_swagger:schema_with_examples( ref(response_user), - response_user_examples()), + response_user_examples() + ), 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), 404 => error_codes([?NOT_FOUND], <<"Not Found">>) } @@ -418,23 +433,28 @@ schema("/authentication/:id/users") -> param_auth_id(), {page, mk(integer(), #{in => query, desc => <<"Page Index">>, required => false})}, {limit, mk(integer(), #{in => query, desc => <<"Page Limit">>, required => false})}, - {like_username, mk(binary(), #{ in => query - , desc => <<"Fuzzy search username">> - , required => false})}, - {like_clientid, mk(binary(), #{ in => query - , desc => <<"Fuzzy search clientid">> - , required => false})} + {like_username, + mk(binary(), #{ + in => query, + desc => <<"Fuzzy search username">>, + required => false + })}, + {like_clientid, + mk(binary(), #{ + in => query, + desc => <<"Fuzzy search clientid">>, + required => false + })} ], responses => #{ 200 => emqx_dashboard_swagger:schema_with_example( ref(response_users), - response_users_example()), + response_users_example() + ), 404 => error_codes([?NOT_FOUND], <<"Not Found">>) } - } }; - schema("/listeners/:listener_id/authentication/:id/users") -> #{ 'operationId' => listener_authenticator_users, @@ -444,11 +464,13 @@ schema("/listeners/:listener_id/authentication/:id/users") -> parameters => [param_auth_id(), param_listener_id()], 'requestBody' => emqx_dashboard_swagger:schema_with_examples( ref(request_user_create), - request_user_create_examples()), + request_user_create_examples() + ), responses => #{ 201 => emqx_dashboard_swagger:schema_with_examples( ref(response_user), - response_user_examples()), + response_user_examples() + ), 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), 404 => error_codes([?NOT_FOUND], <<"Not Found">>) } @@ -457,20 +479,20 @@ schema("/listeners/:listener_id/authentication/:id/users") -> tags => ?API_TAGS_SINGLE, description => <<"List users in authenticator in listener authentication chain">>, parameters => [ - param_listener_id(), param_auth_id(), + param_listener_id(), + param_auth_id(), {page, mk(integer(), #{in => query, desc => <<"Page Index">>, required => false})}, {limit, mk(integer(), #{in => query, desc => <<"Page Limit">>, required => false})} ], responses => #{ 200 => emqx_dashboard_swagger:schema_with_example( ref(response_users), - response_users_example()), + response_users_example() + ), 404 => error_codes([?NOT_FOUND], <<"Not Found">>) } - } }; - schema("/authentication/:id/users/:user_id") -> #{ 'operationId' => authenticator_user, @@ -481,7 +503,8 @@ schema("/authentication/:id/users/:user_id") -> responses => #{ 200 => emqx_dashboard_swagger:schema_with_examples( ref(response_user), - response_user_examples()), + response_user_examples() + ), 404 => error_codes([?NOT_FOUND], <<"Not Found">>) } }, @@ -491,11 +514,13 @@ schema("/authentication/:id/users/:user_id") -> parameters => [param_auth_id(), param_user_id()], 'requestBody' => emqx_dashboard_swagger:schema_with_examples( ref(request_user_update), - request_user_update_examples()), + request_user_update_examples() + ), responses => #{ 200 => emqx_dashboard_swagger:schema_with_example( ref(response_user), - response_user_examples()), + response_user_examples() + ), 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), 404 => error_codes([?NOT_FOUND], <<"Not Found">>) } @@ -510,7 +535,6 @@ schema("/authentication/:id/users/:user_id") -> } } }; - schema("/listeners/:listener_id/authentication/:id/users/:user_id") -> #{ 'operationId' => listener_authenticator_user, @@ -521,7 +545,8 @@ schema("/listeners/:listener_id/authentication/:id/users/:user_id") -> responses => #{ 200 => emqx_dashboard_swagger:schema_with_example( ref(response_user), - response_user_examples()), + response_user_examples() + ), 404 => error_codes([?NOT_FOUND], <<"Not Found">>) } }, @@ -531,15 +556,16 @@ schema("/listeners/:listener_id/authentication/:id/users/:user_id") -> parameters => [param_listener_id(), param_auth_id(), param_user_id()], 'requestBody' => emqx_dashboard_swagger:schema_with_example( ref(request_user_update), - request_user_update_examples()), + request_user_update_examples() + ), responses => #{ 200 => emqx_dashboard_swagger:schema_with_example( ref(response_user), - response_user_examples()), + response_user_examples() + ), 400 => error_codes([?BAD_REQUEST], <<"Bad Request">>), 404 => error_codes([?NOT_FOUND], <<"Not Found">>) } - }, delete => #{ tags => ?API_TAGS_SINGLE, @@ -582,16 +608,13 @@ param_user_id() -> authenticators(post, #{body := Config}) -> create_authenticator([authentication], ?GLOBAL, Config); - authenticators(get, _Params) -> list_authenticators([authentication]). authenticator(get, #{bindings := #{id := AuthenticatorID}}) -> list_authenticator(?GLOBAL, [authentication], AuthenticatorID); - authenticator(put, #{bindings := #{id := AuthenticatorID}, body := Config}) -> update_authenticator([authentication], ?GLOBAL, AuthenticatorID, Config); - authenticator(delete, #{bindings := #{id := AuthenticatorID}}) -> delete_authenticator([authentication], ?GLOBAL, AuthenticatorID). @@ -599,74 +622,118 @@ authenticator_status(get, #{bindings := #{id := AuthenticatorID}}) -> lookup_from_all_nodes(?GLOBAL, AuthenticatorID). listener_authenticators(post, #{bindings := #{listener_id := ListenerID}, body := Config}) -> - with_listener(ListenerID, - fun(Type, Name, ChainName) -> - create_authenticator([listeners, Type, Name, authentication], - ChainName, - Config) - end); - + with_listener( + ListenerID, + fun(Type, Name, ChainName) -> + create_authenticator( + [listeners, Type, Name, authentication], + ChainName, + Config + ) + end + ); listener_authenticators(get, #{bindings := #{listener_id := ListenerID}}) -> - with_listener(ListenerID, - fun(Type, Name, _) -> - list_authenticators([listeners, Type, Name, authentication]) - end). + with_listener( + ListenerID, + fun(Type, Name, _) -> + list_authenticators([listeners, Type, Name, authentication]) + end + ). listener_authenticator(get, #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}}) -> - with_listener(ListenerID, - fun(Type, Name, ChainName) -> - list_authenticator(ChainName, [listeners, Type, Name, authentication], - AuthenticatorID) - end); -listener_authenticator(put, - #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}, - body := Config}) -> - with_listener(ListenerID, - fun(Type, Name, ChainName) -> - update_authenticator([listeners, Type, Name, authentication], - ChainName, - AuthenticatorID, - Config) - end); -listener_authenticator(delete, - #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}}) -> - with_listener(ListenerID, - fun(Type, Name, ChainName) -> - delete_authenticator([listeners, Type, Name, authentication], - ChainName, - AuthenticatorID) - end). + with_listener( + ListenerID, + fun(Type, Name, ChainName) -> + list_authenticator( + ChainName, + [listeners, Type, Name, authentication], + AuthenticatorID + ) + end + ); +listener_authenticator( + put, + #{ + bindings := #{listener_id := ListenerID, id := AuthenticatorID}, + body := Config + } +) -> + with_listener( + ListenerID, + fun(Type, Name, ChainName) -> + update_authenticator( + [listeners, Type, Name, authentication], + ChainName, + AuthenticatorID, + Config + ) + end + ); +listener_authenticator( + delete, + #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}} +) -> + with_listener( + ListenerID, + fun(Type, Name, ChainName) -> + delete_authenticator( + [listeners, Type, Name, authentication], + ChainName, + AuthenticatorID + ) + end + ). -listener_authenticator_status(get, - #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}}) -> - with_listener(ListenerID, - fun(_, _, ChainName) -> - lookup_from_all_nodes(ChainName, AuthenticatorID) - end). +listener_authenticator_status( + get, + #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}} +) -> + with_listener( + ListenerID, + fun(_, _, ChainName) -> + lookup_from_all_nodes(ChainName, AuthenticatorID) + end + ). -authenticator_move(post, - #{bindings := #{id := AuthenticatorID}, - body := #{<<"position">> := Position}}) -> +authenticator_move( + post, + #{ + bindings := #{id := AuthenticatorID}, + body := #{<<"position">> := Position} + } +) -> move_authenticator([authentication], ?GLOBAL, AuthenticatorID, Position); authenticator_move(post, #{bindings := #{id := _}, body := _}) -> serialize_error({missing_parameter, position}). -listener_authenticator_move(post, - #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}, - body := #{<<"position">> := Position}}) -> - with_listener(ListenerID, - fun(Type, Name, ChainName) -> - move_authenticator([listeners, Type, Name, authentication], - ChainName, - AuthenticatorID, - Position) - end); +listener_authenticator_move( + post, + #{ + bindings := #{listener_id := ListenerID, id := AuthenticatorID}, + body := #{<<"position">> := Position} + } +) -> + with_listener( + ListenerID, + fun(Type, Name, ChainName) -> + move_authenticator( + [listeners, Type, Name, authentication], + ChainName, + AuthenticatorID, + Position + ) + end + ); listener_authenticator_move(post, #{bindings := #{listener_id := _, id := _}, body := _}) -> serialize_error({missing_parameter, position}). -authenticator_import_users(post, - #{bindings := #{id := AuthenticatorID}, - body := #{<<"filename">> := Filename}}) -> +authenticator_import_users( + post, + #{ + bindings := #{id := AuthenticatorID}, + body := #{<<"filename">> := Filename} + } +) -> case emqx_authentication:import_users(?GLOBAL, AuthenticatorID, Filename) of ok -> {204}; {error, Reason} -> serialize_error(Reason) @@ -675,17 +742,21 @@ authenticator_import_users(post, #{bindings := #{id := _}, body := _}) -> serialize_error({missing_parameter, filename}). listener_authenticator_import_users( - post, - #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}, - body := #{<<"filename">> := Filename}}) -> + post, + #{ + bindings := #{listener_id := ListenerID, id := AuthenticatorID}, + body := #{<<"filename">> := Filename} + } +) -> with_chain( - ListenerID, - fun(ChainName) -> - case emqx_authentication:import_users(ChainName, AuthenticatorID, Filename) of - ok -> {204}; - {error, Reason} -> serialize_error(Reason) - end - end); + ListenerID, + fun(ChainName) -> + case emqx_authentication:import_users(ChainName, AuthenticatorID, Filename) of + ok -> {204}; + {error, Reason} -> serialize_error(Reason) + end + end + ); listener_authenticator_import_users(post, #{bindings := #{listener_id := _, id := _}, body := _}) -> serialize_error({missing_parameter, filename}). @@ -694,48 +765,86 @@ authenticator_users(post, #{bindings := #{id := AuthenticatorID}, body := UserIn authenticator_users(get, #{bindings := #{id := AuthenticatorID}, query_string := QueryString}) -> list_users(?GLOBAL, AuthenticatorID, QueryString). -authenticator_user(put, #{bindings := #{id := AuthenticatorID, - user_id := UserID}, body := UserInfo}) -> +authenticator_user(put, #{ + bindings := #{ + id := AuthenticatorID, + user_id := UserID + }, + body := UserInfo +}) -> update_user(?GLOBAL, AuthenticatorID, UserID, UserInfo); authenticator_user(get, #{bindings := #{id := AuthenticatorID, user_id := UserID}}) -> find_user(?GLOBAL, AuthenticatorID, UserID); authenticator_user(delete, #{bindings := #{id := AuthenticatorID, user_id := UserID}}) -> delete_user(?GLOBAL, AuthenticatorID, UserID). -listener_authenticator_users(post, #{bindings := #{listener_id := ListenerID, - id := AuthenticatorID}, body := UserInfo}) -> - with_chain(ListenerID, - fun(ChainName) -> - add_user(ChainName, AuthenticatorID, UserInfo) - end); -listener_authenticator_users(get, #{bindings := #{listener_id := ListenerID, - id := AuthenticatorID}, query_string := PageParams}) -> - with_chain(ListenerID, - fun(ChainName) -> - list_users(ChainName, AuthenticatorID, PageParams) - end). +listener_authenticator_users(post, #{ + bindings := #{ + listener_id := ListenerID, + id := AuthenticatorID + }, + body := UserInfo +}) -> + with_chain( + ListenerID, + fun(ChainName) -> + add_user(ChainName, AuthenticatorID, UserInfo) + end + ); +listener_authenticator_users(get, #{ + bindings := #{ + listener_id := ListenerID, + id := AuthenticatorID + }, + query_string := PageParams +}) -> + with_chain( + ListenerID, + fun(ChainName) -> + list_users(ChainName, AuthenticatorID, PageParams) + end + ). -listener_authenticator_user(put, #{bindings := #{listener_id := ListenerID, - id := AuthenticatorID, - user_id := UserID}, body := UserInfo}) -> - with_chain(ListenerID, - fun(ChainName) -> - update_user(ChainName, AuthenticatorID, UserID, UserInfo) - end); -listener_authenticator_user(get, #{bindings := #{listener_id := ListenerID, - id := AuthenticatorID, - user_id := UserID}}) -> - with_chain(ListenerID, - fun(ChainName) -> - find_user(ChainName, AuthenticatorID, UserID) - end); -listener_authenticator_user(delete, #{bindings := #{listener_id := ListenerID, - id := AuthenticatorID, - user_id := UserID}}) -> - with_chain(ListenerID, - fun(ChainName) -> - delete_user(ChainName, AuthenticatorID, UserID) - end). +listener_authenticator_user(put, #{ + bindings := #{ + listener_id := ListenerID, + id := AuthenticatorID, + user_id := UserID + }, + body := UserInfo +}) -> + with_chain( + ListenerID, + fun(ChainName) -> + update_user(ChainName, AuthenticatorID, UserID, UserInfo) + end + ); +listener_authenticator_user(get, #{ + bindings := #{ + listener_id := ListenerID, + id := AuthenticatorID, + user_id := UserID + } +}) -> + with_chain( + ListenerID, + fun(ChainName) -> + find_user(ChainName, AuthenticatorID, UserID) + end + ); +listener_authenticator_user(delete, #{ + bindings := #{ + listener_id := ListenerID, + id := AuthenticatorID, + user_id := UserID + } +}) -> + with_chain( + ListenerID, + fun(ChainName) -> + delete_user(ChainName, AuthenticatorID, UserID) + end + ). %%------------------------------------------------------------------------------ %% Internal functions @@ -768,7 +877,7 @@ find_listener(ListenerID) -> with_chain(ListenerID, Fun) -> {ok, ChainNames} = emqx_authentication:list_chain_names(), ListenerChainName = - [ Name || Name <- ChainNames, atom_to_binary(Name) =:= ListenerID ], + [Name || Name <- ChainNames, atom_to_binary(Name) =:= ListenerID], case ListenerChainName of [ChainName] -> Fun(ChainName); @@ -778,8 +887,10 @@ with_chain(ListenerID, Fun) -> create_authenticator(ConfKeyPath, ChainName, Config) -> case update_config(ConfKeyPath, {create_authenticator, ChainName, Config}) of - {ok, #{post_config_update := #{emqx_authentication := #{id := ID}}, - raw_config := AuthenticatorsConfig}} -> + {ok, #{ + post_config_update := #{emqx_authentication := #{id := ID}}, + raw_config := AuthenticatorsConfig + }} -> {ok, AuthenticatorConfig} = find_config(ID, AuthenticatorsConfig), {200, maps:put(id, ID, convert_certs(fill_defaults(AuthenticatorConfig)))}; {error, {_PrePostConfigUpdate, emqx_authentication, Reason}} -> @@ -790,18 +901,21 @@ create_authenticator(ConfKeyPath, ChainName, Config) -> list_authenticators(ConfKeyPath) -> AuthenticatorsConfig = get_raw_config_with_defaults(ConfKeyPath), - NAuthenticators = [ maps:put( - id, - emqx_authentication:authenticator_id(AuthenticatorConfig), - convert_certs(AuthenticatorConfig)) - || AuthenticatorConfig <- AuthenticatorsConfig], + NAuthenticators = [ + maps:put( + id, + emqx_authentication:authenticator_id(AuthenticatorConfig), + convert_certs(AuthenticatorConfig) + ) + || AuthenticatorConfig <- AuthenticatorsConfig + ], {200, NAuthenticators}. list_authenticator(_, ConfKeyPath, AuthenticatorID) -> AuthenticatorsConfig = get_raw_config_with_defaults(ConfKeyPath), case find_config(AuthenticatorID, AuthenticatorsConfig) of {ok, AuthenticatorConfig} -> - {200, maps:put(id, AuthenticatorID, convert_certs(AuthenticatorConfig))}; + {200, maps:put(id, AuthenticatorID, convert_certs(AuthenticatorConfig))}; {error, Reason} -> serialize_error(Reason) end. @@ -811,24 +925,28 @@ lookup_from_local_node(ChainName, AuthenticatorID) -> case emqx_authentication:lookup_authenticator(ChainName, AuthenticatorID) of {ok, #{provider := Provider, state := State}} -> case lists:member(Provider, resource_provider()) of - false -> {error, {NodeId, resource_unsupport_metrics_and_status}}; + false -> + {error, {NodeId, resource_unsupport_metrics_and_status}}; true -> #{resource_id := ResourceId} = State, case emqx_resource:get_instance(ResourceId) of - {error, not_found} -> {error, {NodeId, not_found_resource}}; - {ok, _, #{ status := Status, metrics := Metrics }} -> + {error, not_found} -> + {error, {NodeId, not_found_resource}}; + {ok, _, #{status := Status, metrics := Metrics}} -> {ok, {NodeId, Status, Metrics}} end end; - {error, Reason} -> {error, {NodeId, Reason}} + {error, Reason} -> + {error, {NodeId, Reason}} end. resource_provider() -> - [ emqx_authn_mysql, - emqx_authn_pgsql, - emqx_authn_mongodb, - emqx_authn_redis, - emqx_authn_http + [ + emqx_authn_mysql, + emqx_authn_pgsql, + emqx_authn_mongodb, + emqx_authn_redis, + emqx_authn_http ]. lookup_from_all_nodes(ChainName, AuthenticatorID) -> @@ -836,38 +954,45 @@ lookup_from_all_nodes(ChainName, AuthenticatorID) -> case is_ok(emqx_authn_proto_v1:lookup_from_all_nodes(Nodes, ChainName, AuthenticatorID)) of {ok, ResList} -> {StatusMap, MetricsMap, _} = make_result_map(ResList), - AggregateStatus = aggregate_status(maps:values(StatusMap)), - AggregateMetrics = aggregate_metrics(maps:values(MetricsMap)), - Fun = fun (_, V1) -> restructure_map(V1) end, - MKMap = fun (Name) -> fun ({Key, Val}) -> #{ node => Key, Name => Val } end end, - HelpFun = fun (M, Name) -> lists:map(MKMap(Name), maps:to_list(M)) end, - case AggregateStatus of - empty_metrics_and_status -> - {400, #{code => <<"BAD_REQUEST">>, - message => <<"Resource Not Support Status">>}}; - _ -> {200, #{node_status => HelpFun(StatusMap, status), - node_metrics => HelpFun(maps:map(Fun, MetricsMap), metrics), - status => AggregateStatus, - metrics => restructure_map(AggregateMetrics) - } - } - end; + AggregateStatus = aggregate_status(maps:values(StatusMap)), + AggregateMetrics = aggregate_metrics(maps:values(MetricsMap)), + Fun = fun(_, V1) -> restructure_map(V1) end, + MKMap = fun(Name) -> fun({Key, Val}) -> #{node => Key, Name => Val} end end, + HelpFun = fun(M, Name) -> lists:map(MKMap(Name), maps:to_list(M)) end, + case AggregateStatus of + empty_metrics_and_status -> + {400, #{ + code => <<"BAD_REQUEST">>, + message => <<"Resource Not Support Status">> + }}; + _ -> + {200, #{ + node_status => HelpFun(StatusMap, status), + node_metrics => HelpFun(maps:map(Fun, MetricsMap), metrics), + status => AggregateStatus, + metrics => restructure_map(AggregateMetrics) + }} + end; {error, ErrL} -> - {500, #{code => <<"INTERNAL_ERROR">>, - message => list_to_binary(io_lib:format("~p", [ErrL]))}} + {500, #{ + code => <<"INTERNAL_ERROR">>, + message => list_to_binary(io_lib:format("~p", [ErrL])) + }} end. -aggregate_status([]) -> empty_metrics_and_status; +aggregate_status([]) -> + empty_metrics_and_status; aggregate_status(AllStatus) -> - Head = fun ([A | _]) -> A end, + Head = fun([A | _]) -> A end, HeadVal = Head(AllStatus), - AllRes = lists:all(fun (Val) -> Val == HeadVal end, AllStatus), + AllRes = lists:all(fun(Val) -> Val == HeadVal end, AllStatus), case AllRes of true -> HeadVal; false -> inconsistent end. -aggregate_metrics([]) -> empty_metrics_and_status; +aggregate_metrics([]) -> + empty_metrics_and_status; aggregate_metrics([HeadMetrics | AllMetrics]) -> CombinerFun = fun ComFun(Val1, Val2) -> @@ -876,8 +1001,9 @@ aggregate_metrics([HeadMetrics | AllMetrics]) -> false -> Val1 + Val2 end end, - Fun = fun (ElemMap, AccMap) -> - emqx_map_lib:merge_with(CombinerFun, ElemMap, AccMap) end, + Fun = fun(ElemMap, AccMap) -> + emqx_map_lib:merge_with(CombinerFun, ElemMap, AccMap) + end, lists:foldl(Fun, HeadMetrics, AllMetrics). make_result_map(ResList) -> @@ -885,48 +1011,57 @@ make_result_map(ResList) -> fun(Elem, {StatusMap, MetricsMap, ErrorMap}) -> case Elem of {ok, {NodeId, Status, Metrics}} -> - {maps:put(NodeId, Status, StatusMap), - maps:put(NodeId, Metrics, MetricsMap), - ErrorMap + { + maps:put(NodeId, Status, StatusMap), + maps:put(NodeId, Metrics, MetricsMap), + ErrorMap }; {error, {NodeId, Reason}} -> - {StatusMap, - MetricsMap, - maps:put(NodeId, Reason, ErrorMap) - } + {StatusMap, MetricsMap, maps:put(NodeId, Reason, ErrorMap)} end end, lists:foldl(Fun, {maps:new(), maps:new(), maps:new()}, ResList). -restructure_map(#{counters := #{failed := Failed, matched := Match, success := Succ}, - rate := #{matched := #{current := Rate, last5m := Rate5m, max := RateMax} - } - } - ) -> - #{matched => Match, - success => Succ, - failed => Failed, - rate => Rate, - rate_last5m => Rate5m, - rate_max => RateMax - }; +restructure_map(#{ + counters := #{failed := Failed, matched := Match, success := Succ}, + rate := #{matched := #{current := Rate, last5m := Rate5m, max := RateMax}} +}) -> + #{ + matched => Match, + success => Succ, + failed => Failed, + rate => Rate, + rate_last5m => Rate5m, + rate_max => RateMax + }; restructure_map(Error) -> - Error. + Error. is_ok(ResL) -> - case lists:filter(fun({ok, _}) -> false; (_) -> true end, ResL) of + case + lists:filter( + fun + ({ok, _}) -> false; + (_) -> true + end, + ResL + ) + of [] -> {ok, [Res || {ok, Res} <- ResL]}; ErrL -> {error, ErrL} end. update_authenticator(ConfKeyPath, ChainName, AuthenticatorID, Config) -> - case update_config(ConfKeyPath, - {update_authenticator, - ChainName, - AuthenticatorID, - Config}) of - {ok, #{post_config_update := #{emqx_authentication := #{id := ID}}, - raw_config := AuthenticatorsConfig}} -> + case + update_config( + ConfKeyPath, + {update_authenticator, ChainName, AuthenticatorID, Config} + ) + of + {ok, #{ + post_config_update := #{emqx_authentication := #{id := ID}}, + raw_config := AuthenticatorsConfig + }} -> {ok, AuthenticatorConfig} = find_config(ID, AuthenticatorsConfig), {200, maps:put(id, ID, convert_certs(fill_defaults(AuthenticatorConfig)))}; {error, {_PrePostConfigUpdate, emqx_authentication, Reason}} -> @@ -948,9 +1083,12 @@ delete_authenticator(ConfKeyPath, ChainName, AuthenticatorID) -> move_authenticator(ConfKeyPath, ChainName, AuthenticatorID, Position) -> case parse_position(Position) of {ok, NPosition} -> - case update_config( - ConfKeyPath, - {move_authenticator, ChainName, AuthenticatorID, NPosition}) of + case + update_config( + ConfKeyPath, + {move_authenticator, ChainName, AuthenticatorID, NPosition} + ) + of {ok, _} -> {204}; {error, {_PrePostConfigUpdate, emqx_authentication, Reason}} -> @@ -962,14 +1100,23 @@ move_authenticator(ConfKeyPath, ChainName, AuthenticatorID, Position) -> serialize_error(Reason) end. -add_user(ChainName, - AuthenticatorID, - #{<<"user_id">> := UserID, <<"password">> := Password} = UserInfo) -> +add_user( + ChainName, + AuthenticatorID, + #{<<"user_id">> := UserID, <<"password">> := Password} = UserInfo +) -> IsSuperuser = maps:get(<<"is_superuser">>, UserInfo, false), - case emqx_authentication:add_user(ChainName, AuthenticatorID, - #{ user_id => UserID - , password => Password - , is_superuser => IsSuperuser}) of + case + emqx_authentication:add_user( + ChainName, + AuthenticatorID, + #{ + user_id => UserID, + password => Password, + is_superuser => IsSuperuser + } + ) + of {ok, User} -> {201, User}; {error, Reason} -> @@ -1015,8 +1162,10 @@ list_users(ChainName, AuthenticatorID, QueryString) -> emqx_mgmt_util:generate_response(Response). update_config(Path, ConfigRequest) -> - emqx_conf:update(Path, ConfigRequest, #{rawconf_with_defaults => true, - override_to => cluster}). + emqx_conf:update(Path, ConfigRequest, #{ + rawconf_with_defaults => true, + override_to => cluster + }). get_raw_config_with_defaults(ConfKeyPath) -> NConfKeyPath = [atom_to_binary(Key, utf8) || Key <- ConfKeyPath], @@ -1024,10 +1173,12 @@ get_raw_config_with_defaults(ConfKeyPath) -> ensure_list(fill_defaults(RawConfig)). find_config(AuthenticatorID, AuthenticatorsConfig) -> - MatchingACs - = [AC - || AC <- ensure_list(AuthenticatorsConfig), - AuthenticatorID =:= emqx_authentication:authenticator_id(AC)], + MatchingACs = + [ + AC + || AC <- ensure_list(AuthenticatorsConfig), + AuthenticatorID =:= emqx_authentication:authenticator_id(AC) + ], case MatchingACs of [] -> {error, {not_found, {authenticator, AuthenticatorID}}}; [AuthenticatorConfig] -> {ok, AuthenticatorConfig} @@ -1039,69 +1190,108 @@ fill_defaults(Config) -> emqx_authn:check_config(Config, #{only_fill_defaults => true}). convert_certs(#{ssl := #{enable := true} = SSLOpts} = Config) -> - NSSLOpts = lists:foldl(fun(K, Acc) -> - case maps:get(K, Acc, undefined) of - undefined -> Acc; - Filename -> - {ok, Bin} = file:read_file(Filename), - Acc#{K => Bin} - end - end, SSLOpts, [certfile, keyfile, cacertfile]), + NSSLOpts = lists:foldl( + fun(K, Acc) -> + case maps:get(K, Acc, undefined) of + undefined -> + Acc; + Filename -> + {ok, Bin} = file:read_file(Filename), + Acc#{K => Bin} + end + end, + SSLOpts, + [certfile, keyfile, cacertfile] + ), Config#{ssl => NSSLOpts}; convert_certs(Config) -> Config. serialize_error({user_error, not_found}) -> - {404, #{code => <<"NOT_FOUND">>, - message => binfmt("User not found", [])}}; + {404, #{ + code => <<"NOT_FOUND">>, + message => binfmt("User not found", []) + }}; serialize_error({user_error, already_exist}) -> - {409, #{code => <<"ALREADY_EXISTS">>, - message => binfmt("User already exists", [])}}; + {409, #{ + code => <<"ALREADY_EXISTS">>, + message => binfmt("User already exists", []) + }}; serialize_error({user_error, Reason}) -> - {400, #{code => <<"BAD_REQUEST">>, - message => binfmt("User error: ~p", [Reason])}}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => binfmt("User error: ~p", [Reason]) + }}; serialize_error({not_found, {authenticator, ID}}) -> - {404, #{code => <<"NOT_FOUND">>, - message => binfmt("Authenticator '~ts' does not exist", [ID]) }}; + {404, #{ + code => <<"NOT_FOUND">>, + message => binfmt("Authenticator '~ts' does not exist", [ID]) + }}; serialize_error({not_found, {listener, ID}}) -> - {404, #{code => <<"NOT_FOUND">>, - message => binfmt("Listener '~ts' does not exist", [ID])}}; + {404, #{ + code => <<"NOT_FOUND">>, + message => binfmt("Listener '~ts' does not exist", [ID]) + }}; serialize_error({not_found, {chain, ?GLOBAL}}) -> - {404, #{code => <<"NOT_FOUND">>, - message => <<"Authenticator not found in the 'global' scope">>}}; + {404, #{ + code => <<"NOT_FOUND">>, + message => <<"Authenticator not found in the 'global' scope">> + }}; serialize_error({not_found, {chain, Name}}) -> - {400, #{code => <<"BAD_REQUEST">>, - message => binfmt("No authentication has been created for listener ~p", [Name])}}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => binfmt("No authentication has been created for listener ~p", [Name]) + }}; serialize_error({already_exists, {authenticator, ID}}) -> - {409, #{code => <<"ALREADY_EXISTS">>, - message => binfmt("Authenticator '~ts' already exist", [ID])}}; + {409, #{ + code => <<"ALREADY_EXISTS">>, + message => binfmt("Authenticator '~ts' already exist", [ID]) + }}; serialize_error(no_available_provider) -> - {400, #{code => <<"BAD_REQUEST">>, - message => <<"Unsupported authentication type">>}}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => <<"Unsupported authentication type">> + }}; serialize_error(change_of_authentication_type_is_not_allowed) -> - {400, #{code => <<"BAD_REQUEST">>, - message => <<"Change of authentication type is not allowed">>}}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => <<"Change of authentication type is not allowed">> + }}; serialize_error(unsupported_operation) -> - {400, #{code => <<"BAD_REQUEST">>, - message => <<"Operation not supported in this authentication type">>}}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => <<"Operation not supported in this authentication type">> + }}; serialize_error({bad_ssl_config, Details}) -> - {400, #{code => <<"BAD_REQUEST">>, - message => binfmt("bad_ssl_config ~p", [Details])}}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => binfmt("bad_ssl_config ~p", [Details]) + }}; serialize_error({missing_parameter, Detail}) -> - {400, #{code => <<"BAD_REQUEST">>, - message => binfmt("Missing required parameter: ~p", [Detail])}}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => binfmt("Missing required parameter: ~p", [Detail]) + }}; serialize_error({invalid_parameter, Name}) -> - {400, #{code => <<"BAD_REQUEST">>, - message => binfmt("Invalid value for '~p'", [Name])}}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => binfmt("Invalid value for '~p'", [Name]) + }}; serialize_error({unknown_authn_type, Type}) -> - {400, #{code => <<"BAD_REQUEST">>, - message => binfmt("Unknown type '~p'", [Type])}}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => binfmt("Unknown type '~p'", [Type]) + }}; serialize_error({bad_authenticator_config, Reason}) -> - {400, #{code => <<"BAD_REQUEST">>, - message => binfmt("Bad authenticator config ~p", [Reason])}}; + {400, #{ + code => <<"BAD_REQUEST">>, + message => binfmt("Bad authenticator config ~p", [Reason]) + }}; serialize_error(Reason) -> - {400, #{code => <<"BAD_REQUEST">>, - message => binfmt("~p", [Reason])}}. + {400, #{ + code => <<"BAD_REQUEST">>, + message => binfmt("~p", [Reason]) + }}. parse_position(<<"front">>) -> {ok, ?CMD_MOVE_FRONT}; @@ -1212,29 +1402,36 @@ authenticator_examples() -> }. status_metrics_example() -> - #{ metrics => #{ matched => 0, - success => 0, - failed => 0, - rate => 0.0, - rate_last5m => 0.0, - rate_max => 0.0 - }, - node_metrics => [ #{node => node(), - metrics => #{ matched => 0, - success => 0, - failed => 0, - rate => 0.0, - rate_last5m => 0.0, - rate_max => 0.0 - } - } - ], - status => connected, - node_status => [ #{node => node(), - status => connected - } - ] - }. + #{ + metrics => #{ + matched => 0, + success => 0, + failed => 0, + rate => 0.0, + rate_last5m => 0.0, + rate_max => 0.0 + }, + node_metrics => [ + #{ + node => node(), + metrics => #{ + matched => 0, + success => 0, + failed => 0, + rate => 0.0, + rate_last5m => 0.0, + rate_max => 0.0 + } + } + ], + status => connected, + node_status => [ + #{ + node => node(), + status => connected + } + ] + }. request_user_create_examples() -> #{ diff --git a/apps/emqx_authn/src/emqx_authn_app.erl b/apps/emqx_authn/src/emqx_authn_app.erl index c5a3e73bd..f761bfe33 100644 --- a/apps/emqx_authn/src/emqx_authn_app.erl +++ b/apps/emqx_authn/src/emqx_authn_app.erl @@ -21,9 +21,10 @@ -behaviour(application). %% Application callbacks --export([ start/2 - , stop/1 - ]). +-export([ + start/2, + stop/1 +]). -include_lib("emqx/include/emqx_authentication.hrl"). @@ -51,13 +52,15 @@ initialize() -> ok = ?AUTHN:register_providers(emqx_authn:providers()), lists:foreach( - fun({ChainName, RawAuthConfigs}) -> - AuthConfig = emqx_authn:check_configs(RawAuthConfigs), - ?AUTHN:initialize_authentication( - ChainName, - AuthConfig) - end, - chain_configs()). + fun({ChainName, RawAuthConfigs}) -> + AuthConfig = emqx_authn:check_configs(RawAuthConfigs), + ?AUTHN:initialize_authentication( + ChainName, + AuthConfig + ) + end, + chain_configs() + ). deinitialize() -> ok = ?AUTHN:deregister_providers(provider_types()), @@ -71,15 +74,16 @@ global_chain_config() -> listener_chain_configs() -> lists:map( - fun({ListenerID, _}) -> - {ListenerID, emqx:get_raw_config(auth_config_path(ListenerID), [])} - end, - emqx_listeners:list()). + fun({ListenerID, _}) -> + {ListenerID, emqx:get_raw_config(auth_config_path(ListenerID), [])} + end, + emqx_listeners:list() + ). auth_config_path(ListenerID) -> - [<<"listeners">>] - ++ binary:split(atom_to_binary(ListenerID), <<":">>) - ++ [?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY]. + [<<"listeners">>] ++ + binary:split(atom_to_binary(ListenerID), <<":">>) ++ + [?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_BINARY]. provider_types() -> lists:map(fun({Type, _Module}) -> Type end, emqx_authn:providers()). diff --git a/apps/emqx_authn/src/emqx_authn_password_hashing.erl b/apps/emqx_authn/src/emqx_authn_password_hashing.erl index 7a3573ca9..7435fe9c9 100644 --- a/apps/emqx_authn/src/emqx_authn_password_hashing.erl +++ b/apps/emqx_authn/src/emqx_authn_password_hashing.erl @@ -18,21 +18,25 @@ -include_lib("typerefl/include/types.hrl"). --type(simple_algorithm_name() :: plain | md5 | sha | sha256 | sha512). --type(salt_position() :: prefix | suffix). +-type simple_algorithm_name() :: plain | md5 | sha | sha256 | sha512. +-type salt_position() :: prefix | suffix. --type(simple_algorithm() :: #{name := simple_algorithm_name(), - salt_position := salt_position()}). +-type simple_algorithm() :: #{ + name := simple_algorithm_name(), + salt_position := salt_position() +}. --type(bcrypt_algorithm() :: #{name := bcrypt}). --type(bcrypt_algorithm_rw() :: #{name := bcrypt, salt_rounds := integer()}). +-type bcrypt_algorithm() :: #{name := bcrypt}. +-type bcrypt_algorithm_rw() :: #{name := bcrypt, salt_rounds := integer()}. --type(pbkdf2_algorithm() :: #{name := pbkdf2, - mac_fun := emqx_passwd:pbkdf2_mac_fun(), - iterations := pos_integer()}). +-type pbkdf2_algorithm() :: #{ + name := pbkdf2, + mac_fun := emqx_passwd:pbkdf2_mac_fun(), + iterations := pos_integer() +}. --type(algorithm() :: simple_algorithm() | pbkdf2_algorithm() | bcrypt_algorithm()). --type(algorithm_rw() :: simple_algorithm() | pbkdf2_algorithm() | bcrypt_algorithm_rw()). +-type algorithm() :: simple_algorithm() | pbkdf2_algorithm() | bcrypt_algorithm(). +-type algorithm_rw() :: simple_algorithm() | pbkdf2_algorithm() | bcrypt_algorithm_rw(). %%------------------------------------------------------------------------------ %% Hocon Schema @@ -40,37 +44,44 @@ -behaviour(hocon_schema). --export([roots/0, - fields/1, - namespace/0]). +-export([ + roots/0, + fields/1, + namespace/0 +]). --export([type_ro/1, - type_rw/1]). +-export([ + type_ro/1, + type_rw/1 +]). --export([init/1, - gen_salt/1, - hash/2, - check_password/4]). +-export([ + init/1, + gen_salt/1, + hash/2, + check_password/4 +]). namespace() -> "authn-hash". roots() -> [pbkdf2, bcrypt, bcrypt_rw, other_algorithms]. fields(bcrypt_rw) -> fields(bcrypt) ++ - [{salt_rounds, fun salt_rounds/1}]; - + [{salt_rounds, fun salt_rounds/1}]; fields(bcrypt) -> [{name, {enum, [bcrypt]}}]; - fields(pbkdf2) -> - [{name, {enum, [pbkdf2]}}, - {mac_fun, {enum, [md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512]}}, - {iterations, integer()}, - {dk_length, fun dk_length/1}]; - + [ + {name, {enum, [pbkdf2]}}, + {mac_fun, {enum, [md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512]}}, + {iterations, integer()}, + {dk_length, fun dk_length/1} + ]; fields(other_algorithms) -> - [{name, {enum, [plain, md5, sha, sha256, sha512]}}, - {salt_position, fun salt_position/1}]. + [ + {name, {enum, [plain, md5, sha, sha256, sha512]}}, + {salt_position, fun salt_position/1} + ]. salt_position(type) -> {enum, [prefix, suffix]}; salt_position(desc) -> "Specifies whether the password salt is stored as a prefix or the suffix."; @@ -89,47 +100,56 @@ dk_length(_) -> undefined. type_rw(type) -> hoconsc:union(rw_refs()); -type_rw(default) -> #{<<"name">> => sha256, <<"salt_position">> => prefix}; -type_rw(_) -> undefined. +type_rw(default) -> + #{<<"name">> => sha256, <<"salt_position">> => prefix}; +type_rw(_) -> + undefined. type_ro(type) -> hoconsc:union(ro_refs()); -type_ro(default) -> #{<<"name">> => sha256, <<"salt_position">> => prefix}; -type_ro(_) -> undefined. +type_ro(default) -> + #{<<"name">> => sha256, <<"salt_position">> => prefix}; +type_ro(_) -> + undefined. %%------------------------------------------------------------------------------ %% APIs %%------------------------------------------------------------------------------ --spec(init(algorithm()) -> ok). +-spec init(algorithm()) -> ok. init(#{name := bcrypt}) -> {ok, _} = application:ensure_all_started(bcrypt), ok; init(#{name := _Other}) -> ok. - --spec(gen_salt(algorithm_rw()) -> emqx_passwd:salt()). +-spec gen_salt(algorithm_rw()) -> emqx_passwd:salt(). gen_salt(#{name := plain}) -> <<>>; -gen_salt(#{name := bcrypt, - salt_rounds := Rounds}) -> +gen_salt(#{ + name := bcrypt, + salt_rounds := Rounds +}) -> {ok, Salt} = bcrypt:gen_salt(Rounds), list_to_binary(Salt); gen_salt(#{name := Other}) when Other =/= plain, Other =/= bcrypt -> <> = crypto:strong_rand_bytes(16), iolist_to_binary(io_lib:format("~32.16.0b", [X])). - --spec(hash(algorithm_rw(), emqx_passwd:password()) -> {emqx_passwd:hash(), emqx_passwd:salt()}). +-spec hash(algorithm_rw(), emqx_passwd:password()) -> {emqx_passwd:hash(), emqx_passwd:salt()}. hash(#{name := bcrypt, salt_rounds := _} = Algorithm, Password) -> Salt0 = gen_salt(Algorithm), Hash = emqx_passwd:hash({bcrypt, Salt0}, Password), Salt = Hash, {Hash, Salt}; -hash(#{name := pbkdf2, - mac_fun := MacFun, - iterations := Iterations} = Algorithm, Password) -> +hash( + #{ + name := pbkdf2, + mac_fun := MacFun, + iterations := Iterations + } = Algorithm, + Password +) -> Salt = gen_salt(Algorithm), DKLength = maps:get(dk_length, Algorithm, undefined), Hash = emqx_passwd:hash({pbkdf2, MacFun, Salt, Iterations, DKLength}, Password), @@ -139,18 +159,24 @@ hash(#{name := Other, salt_position := SaltPosition} = Algorithm, Password) -> Hash = emqx_passwd:hash({Other, Salt, SaltPosition}, Password), {Hash, Salt}. - --spec(check_password( - algorithm(), - emqx_passwd:salt(), - emqx_passwd:hash(), - emqx_passwd:password()) -> boolean()). +-spec check_password( + algorithm(), + emqx_passwd:salt(), + emqx_passwd:hash(), + emqx_passwd:password() +) -> boolean(). check_password(#{name := bcrypt}, _Salt, PasswordHash, Password) -> emqx_passwd:check_pass({bcrypt, PasswordHash}, PasswordHash, Password); -check_password(#{name := pbkdf2, - mac_fun := MacFun, - iterations := Iterations} = Algorithm, - Salt, PasswordHash, Password) -> +check_password( + #{ + name := pbkdf2, + mac_fun := MacFun, + iterations := Iterations + } = Algorithm, + Salt, + PasswordHash, + Password +) -> DKLength = maps:get(dk_length, Algorithm, undefined), emqx_passwd:check_pass({pbkdf2, MacFun, Salt, Iterations, DKLength}, PasswordHash, Password); check_password(#{name := Other, salt_position := SaltPosition}, Salt, PasswordHash, Password) -> @@ -161,11 +187,15 @@ check_password(#{name := Other, salt_position := SaltPosition}, Salt, PasswordHa %%------------------------------------------------------------------------------ rw_refs() -> - [hoconsc:ref(?MODULE, bcrypt_rw), - hoconsc:ref(?MODULE, pbkdf2), - hoconsc:ref(?MODULE, other_algorithms)]. + [ + hoconsc:ref(?MODULE, bcrypt_rw), + hoconsc:ref(?MODULE, pbkdf2), + hoconsc:ref(?MODULE, other_algorithms) + ]. ro_refs() -> - [hoconsc:ref(?MODULE, bcrypt), - hoconsc:ref(?MODULE, pbkdf2), - hoconsc:ref(?MODULE, other_algorithms)]. + [ + hoconsc:ref(?MODULE, bcrypt), + hoconsc:ref(?MODULE, pbkdf2), + hoconsc:ref(?MODULE, other_algorithms) + ]. diff --git a/apps/emqx_authn/src/emqx_authn_schema.erl b/apps/emqx_authn/src/emqx_authn_schema.erl index b8f8dacfd..0c227973b 100644 --- a/apps/emqx_authn/src/emqx_authn_schema.erl +++ b/apps/emqx_authn/src/emqx_authn_schema.erl @@ -20,21 +20,20 @@ -include_lib("typerefl/include/types.hrl"). -import(hoconsc, [mk/2, ref/2]). --export([ common_fields/0 - , roots/0 - , fields/1 - , authenticator_type/0 - , root_type/0 - , mechanism/1 - , backend/1 - ]). +-export([ + common_fields/0, + roots/0, + fields/1, + authenticator_type/0, + root_type/0, + mechanism/1, + backend/1 +]). roots() -> []. - common_fields() -> - [ {enable, fun enable/1} - ]. + [{enable, fun enable/1}]. enable(type) -> boolean(); enable(default) -> true; @@ -54,46 +53,60 @@ root_type() -> hoconsc:array(authenticator_type()). mechanism(Name) -> - hoconsc:mk(hoconsc:enum([Name]), - #{ required => true - , desc => "Authentication mechanism." - }). + hoconsc:mk( + hoconsc:enum([Name]), + #{ + required => true, + desc => "Authentication mechanism." + } + ). backend(Name) -> - hoconsc:mk(hoconsc:enum([Name]), - #{ required => true - , desc => "Backend type." - }). + hoconsc:mk( + hoconsc:enum([Name]), + #{ + required => true, + desc => "Backend type." + } + ). fields("metrics_status_fields") -> - [ {"metrics", mk(ref(?MODULE, "metrics"), #{desc => "The metrics of the resource"})} - , {"node_metrics", mk(hoconsc:array(ref(?MODULE, "node_metrics")), - #{ desc => "The metrics of the resource for each node" - })} - , {"status", mk(status(), #{desc => "The status of the resource"})} - , {"node_status", mk(hoconsc:array(ref(?MODULE, "node_status")), - #{ desc => "The status of the resource for each node" - })} + [ + {"metrics", mk(ref(?MODULE, "metrics"), #{desc => "The metrics of the resource"})}, + {"node_metrics", + mk( + hoconsc:array(ref(?MODULE, "node_metrics")), + #{desc => "The metrics of the resource for each node"} + )}, + {"status", mk(status(), #{desc => "The status of the resource"})}, + {"node_status", + mk( + hoconsc:array(ref(?MODULE, "node_status")), + #{desc => "The status of the resource for each node"} + )} ]; - fields("metrics") -> - [ {"matched", mk(integer(), #{desc => "Count of this resource is queried"})} - , {"success", mk(integer(), #{desc => "Count of query success"})} - , {"failed", mk(integer(), #{desc => "Count of query failed"})} - , {"rate", mk(float(), #{desc => "The rate of matched, times/second"})} - , {"rate_max", mk(float(), #{desc => "The max rate of matched, times/second"})} - , {"rate_last5m", mk(float(), - #{desc => "The average rate of matched in the last 5 minutes, times/second"})} + [ + {"matched", mk(integer(), #{desc => "Count of this resource is queried"})}, + {"success", mk(integer(), #{desc => "Count of query success"})}, + {"failed", mk(integer(), #{desc => "Count of query failed"})}, + {"rate", mk(float(), #{desc => "The rate of matched, times/second"})}, + {"rate_max", mk(float(), #{desc => "The max rate of matched, times/second"})}, + {"rate_last5m", + mk( + float(), + #{desc => "The average rate of matched in the last 5 minutes, times/second"} + )} ]; - fields("node_metrics") -> - [ node_name() - , {"metrics", mk(ref(?MODULE, "metrics"), #{})} + [ + node_name(), + {"metrics", mk(ref(?MODULE, "metrics"), #{})} ]; - fields("node_status") -> - [ node_name() - , {"status", mk(status(), #{desc => "Status of the node."})} + [ + node_name(), + {"status", mk(status(), #{desc => "Status of the node."})} ]. status() -> diff --git a/apps/emqx_authn/src/emqx_authn_sup.erl b/apps/emqx_authn/src/emqx_authn_sup.erl index 168d60db3..d88acfacc 100644 --- a/apps/emqx_authn/src/emqx_authn_sup.erl +++ b/apps/emqx_authn/src/emqx_authn_sup.erl @@ -18,9 +18,10 @@ -behaviour(supervisor). --export([ start_link/0 - , init/1 - ]). +-export([ + start_link/0, + init/1 +]). start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). diff --git a/apps/emqx_authn/src/emqx_authn_utils.erl b/apps/emqx_authn/src/emqx_authn_utils.erl index 7f17215c1..5f2e0100e 100644 --- a/apps/emqx_authn/src/emqx_authn_utils.erl +++ b/apps/emqx_authn/src/emqx_authn_utils.erl @@ -19,26 +19,29 @@ -include_lib("emqx/include/emqx_placeholder.hrl"). -include_lib("emqx_authn.hrl"). --export([ check_password_from_selected_map/3 - , parse_deep/1 - , parse_str/1 - , parse_sql/2 - , render_deep/2 - , render_str/2 - , render_sql_params/2 - , is_superuser/1 - , bin/1 - , ensure_apps_started/1 - , cleanup_resources/0 - , make_resource_id/1 - ]). +-export([ + check_password_from_selected_map/3, + parse_deep/1, + parse_str/1, + parse_sql/2, + render_deep/2, + render_str/2, + render_sql_params/2, + is_superuser/1, + bin/1, + ensure_apps_started/1, + cleanup_resources/0, + make_resource_id/1 +]). --define(AUTHN_PLACEHOLDERS, [?PH_USERNAME, - ?PH_CLIENTID, - ?PH_PASSWORD, - ?PH_PEERHOST, - ?PH_CERT_SUBJECT, - ?PH_CERT_CN_NAME]). +-define(AUTHN_PLACEHOLDERS, [ + ?PH_USERNAME, + ?PH_CLIENTID, + ?PH_PASSWORD, + ?PH_PEERHOST, + ?PH_CERT_SUBJECT, + ?PH_CERT_CN_NAME +]). %%------------------------------------------------------------------------------ %% APIs @@ -47,12 +50,12 @@ check_password_from_selected_map(_Algorithm, _Selected, undefined) -> {error, bad_username_or_password}; check_password_from_selected_map( - Algorithm, #{<<"password_hash">> := Hash} = Selected, Password) -> + Algorithm, #{<<"password_hash">> := Hash} = Selected, Password +) -> Salt = maps:get(<<"salt">>, Selected, <<>>), case emqx_authn_password_hashing:check_password(Algorithm, Salt, Hash, Password) of true -> ok; - false -> - {error, bad_username_or_password} + false -> {error, bad_username_or_password} end. parse_deep(Template) -> @@ -63,27 +66,33 @@ parse_str(Template) -> parse_sql(Template, ReplaceWith) -> emqx_placeholder:preproc_sql( - Template, - #{replace_with => ReplaceWith, - placeholders => ?AUTHN_PLACEHOLDERS}). + Template, + #{ + replace_with => ReplaceWith, + placeholders => ?AUTHN_PLACEHOLDERS + } + ). render_deep(Template, Credential) -> emqx_placeholder:proc_tmpl_deep( - Template, - Credential, - #{return => full_binary, var_trans => fun handle_var/2}). + Template, + Credential, + #{return => full_binary, var_trans => fun handle_var/2} + ). render_str(Template, Credential) -> emqx_placeholder:proc_tmpl( - Template, - Credential, - #{return => full_binary, var_trans => fun handle_var/2}). + Template, + Credential, + #{return => full_binary, var_trans => fun handle_var/2} + ). render_sql_params(ParamList, Credential) -> emqx_placeholder:proc_tmpl( - ParamList, - Credential, - #{return => rawlist, var_trans => fun handle_sql_var/2}). + ParamList, + Credential, + #{return => rawlist, var_trans => fun handle_sql_var/2} + ). is_superuser(#{<<"is_superuser">> := <<"">>}) -> #{is_superuser => false}; @@ -114,8 +123,9 @@ bin(X) -> X. cleanup_resources() -> lists:foreach( - fun emqx_resource:remove_local/1, - emqx_resource:list_group_instances(?RESOURCE_GROUP)). + fun emqx_resource:remove_local/1, + emqx_resource:list_group_instances(?RESOURCE_GROUP) + ). make_resource_id(Name) -> NameBin = bin(Name), diff --git a/apps/emqx_authn/test/emqx_authn_api_SUITE.erl b/apps/emqx_authn/test/emqx_authn_api_SUITE.erl index c28a9dc33..7889143f3 100644 --- a/apps/emqx_authn/test/emqx_authn_api_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_api_SUITE.erl @@ -26,12 +26,12 @@ -define(TCP_DEFAULT, 'tcp:default'). --define( - assertAuthenticatorsMatch(Guard, Path), +-define(assertAuthenticatorsMatch(Guard, Path), (fun() -> {ok, 200, Response} = request(get, uri(Path)), ?assertMatch(Guard, jiffy:decode(Response, [return_maps])) - end)()). + end)() +). all() -> emqx_common_test_helpers:all(?MODULE). @@ -42,12 +42,14 @@ groups() -> init_per_testcase(_, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), emqx_authn_test_lib:delete_authenticators( - [?CONF_NS_ATOM], - ?GLOBAL), + [?CONF_NS_ATOM], + ?GLOBAL + ), emqx_authn_test_lib:delete_authenticators( - [listeners, tcp, default, ?CONF_NS_ATOM], - ?TCP_DEFAULT), + [listeners, tcp, default, ?CONF_NS_ATOM], + ?TCP_DEFAULT + ), {atomic, ok} = mria:clear_table(emqx_authn_mnesia), Config. @@ -55,8 +57,9 @@ init_per_testcase(_, Config) -> init_per_suite(Config) -> _ = application:load(emqx_conf), ok = emqx_common_test_helpers:start_apps( - [emqx_authn, emqx_dashboard], - fun set_special_configs/1), + [emqx_authn, emqx_dashboard], + fun set_special_configs/1 + ), ?AUTHN:delete_chain(?GLOBAL), {ok, Chains} = ?AUTHN:list_chains(), @@ -117,108 +120,132 @@ t_listener_authenticator_import_users(_) -> test_authenticator_import_users(["listeners", ?TCP_DEFAULT]). test_authenticators(PathPrefix) -> - ValidConfig = emqx_authn_test_lib:http_example(), {ok, 200, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS]), - ValidConfig), + post, + uri(PathPrefix ++ [?CONF_NS]), + ValidConfig + ), {ok, 409, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS]), - ValidConfig), + post, + uri(PathPrefix ++ [?CONF_NS]), + ValidConfig + ), InvalidConfig0 = ValidConfig#{method => <<"delete">>}, {ok, 400, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS]), - InvalidConfig0), + post, + uri(PathPrefix ++ [?CONF_NS]), + InvalidConfig0 + ), - InvalidConfig1 = ValidConfig#{method => <<"get">>, - headers => #{<<"content-type">> => <<"application/json">>}}, + InvalidConfig1 = ValidConfig#{ + method => <<"get">>, + headers => #{<<"content-type">> => <<"application/json">>} + }, {ok, 400, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS]), - InvalidConfig1), + post, + uri(PathPrefix ++ [?CONF_NS]), + InvalidConfig1 + ), ?assertAuthenticatorsMatch( - [#{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}], - PathPrefix ++ [?CONF_NS]). + [#{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}], + PathPrefix ++ [?CONF_NS] + ). test_authenticator(PathPrefix) -> ValidConfig0 = emqx_authn_test_lib:http_example(), {ok, 200, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS]), - ValidConfig0), + post, + uri(PathPrefix ++ [?CONF_NS]), + ValidConfig0 + ), {ok, 200, _} = request( - get, - uri(PathPrefix ++ [?CONF_NS, "password_based:http"])), + get, + uri(PathPrefix ++ [?CONF_NS, "password_based:http"]) + ), {ok, 200, Res} = request( - get, - uri(PathPrefix ++ [?CONF_NS, "password_based:http", "status"])), + get, + uri(PathPrefix ++ [?CONF_NS, "password_based:http", "status"]) + ), {ok, RList} = emqx_json:safe_decode(Res), - Snd = fun ({_, Val}) -> Val end, + Snd = fun({_, Val}) -> Val end, LookupVal = fun LookupV(List, RestJson) -> - case List of - [Name] -> Snd(lists:keyfind(Name, 1, RestJson)); - [Name | NS] -> LookupV(NS, Snd(lists:keyfind(Name, 1, RestJson))) - end - end, - LookFun = fun (List) -> LookupVal(List, RList) end, - MetricsList = [{<<"failed">>, 0}, - {<<"matched">>, 0}, - {<<"rate">>, 0.0}, - {<<"rate_last5m">>, 0.0}, - {<<"rate_max">>, 0.0}, - {<<"success">>, 0}], - EqualFun = fun ({M, V}) -> - ?assertEqual(V, LookFun([<<"metrics">>, - M] - ) - ) end, + case List of + [Name] -> Snd(lists:keyfind(Name, 1, RestJson)); + [Name | NS] -> LookupV(NS, Snd(lists:keyfind(Name, 1, RestJson))) + end + end, + LookFun = fun(List) -> LookupVal(List, RList) end, + MetricsList = [ + {<<"failed">>, 0}, + {<<"matched">>, 0}, + {<<"rate">>, 0.0}, + {<<"rate_last5m">>, 0.0}, + {<<"rate_max">>, 0.0}, + {<<"success">>, 0} + ], + EqualFun = fun({M, V}) -> + ?assertEqual( + V, + LookFun([ + <<"metrics">>, + M + ]) + ) + end, lists:map(EqualFun, MetricsList), - ?assertEqual(<<"connected">>, - LookFun([<<"status">> - ])), + ?assertEqual( + <<"connected">>, + LookFun([<<"status">>]) + ), {ok, 404, _} = request( - get, - uri(PathPrefix ++ [?CONF_NS, "password_based:redis"])), - + get, + uri(PathPrefix ++ [?CONF_NS, "password_based:redis"]) + ), {ok, 404, _} = request( - put, - uri(PathPrefix ++ [?CONF_NS, "password_based:built_in_database"]), - emqx_authn_test_lib:built_in_database_example()), + put, + uri(PathPrefix ++ [?CONF_NS, "password_based:built_in_database"]), + emqx_authn_test_lib:built_in_database_example() + ), InvalidConfig0 = ValidConfig0#{method => <<"delete">>}, {ok, 400, _} = request( - put, - uri(PathPrefix ++ [?CONF_NS, "password_based:http"]), - InvalidConfig0), + put, + uri(PathPrefix ++ [?CONF_NS, "password_based:http"]), + InvalidConfig0 + ), - InvalidConfig1 = ValidConfig0#{method => <<"get">>, - headers => #{<<"content-type">> => <<"application/json">>}}, + InvalidConfig1 = ValidConfig0#{ + method => <<"get">>, + headers => #{<<"content-type">> => <<"application/json">>} + }, {ok, 400, _} = request( - put, - uri(PathPrefix ++ [?CONF_NS, "password_based:http"]), - InvalidConfig1), + put, + uri(PathPrefix ++ [?CONF_NS, "password_based:http"]), + InvalidConfig1 + ), ValidConfig1 = ValidConfig0#{pool_size => 9}, {ok, 200, _} = request( - put, - uri(PathPrefix ++ [?CONF_NS, "password_based:http"]), - ValidConfig1), + put, + uri(PathPrefix ++ [?CONF_NS, "password_based:http"]), + ValidConfig1 + ), {ok, 404, _} = request( - delete, - uri(PathPrefix ++ [?CONF_NS, "password_based:redis"])), + delete, + uri(PathPrefix ++ [?CONF_NS, "password_based:redis"]) + ), {ok, 204, _} = request( - delete, - uri(PathPrefix ++ [?CONF_NS, "password_based:http"])), + delete, + uri(PathPrefix ++ [?CONF_NS, "password_based:http"]) + ), ?assertAuthenticatorsMatch([], PathPrefix ++ [?CONF_NS]). @@ -226,64 +253,78 @@ test_authenticator_users(PathPrefix) -> UsersUri = uri(PathPrefix ++ [?CONF_NS, "password_based:built_in_database", "users"]), {ok, 200, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS]), - emqx_authn_test_lib:built_in_database_example()), + post, + uri(PathPrefix ++ [?CONF_NS]), + emqx_authn_test_lib:built_in_database_example() + ), InvalidUsers = [ - #{clientid => <<"u1">>, password => <<"p1">>}, - #{user_id => <<"u2">>}, - #{user_id => <<"u3">>, password => <<"p3">>, foobar => <<"foobar">>}], + #{clientid => <<"u1">>, password => <<"p1">>}, + #{user_id => <<"u2">>}, + #{user_id => <<"u3">>, password => <<"p3">>, foobar => <<"foobar">>} + ], lists:foreach( - fun(User) -> {ok, 400, _} = request(post, UsersUri, User) end, - InvalidUsers), - + fun(User) -> {ok, 400, _} = request(post, UsersUri, User) end, + InvalidUsers + ), ValidUsers = [ - #{user_id => <<"u1">>, password => <<"p1">>}, - #{user_id => <<"u2">>, password => <<"p2">>, is_superuser => true}, - #{user_id => <<"u3">>, password => <<"p3">>}], + #{user_id => <<"u1">>, password => <<"p1">>}, + #{user_id => <<"u2">>, password => <<"p2">>, is_superuser => true}, + #{user_id => <<"u3">>, password => <<"p3">>} + ], lists:foreach( - fun(User) -> - {ok, 201, UserData} = request(post, UsersUri, User), - CreatedUser = jiffy:decode(UserData, [return_maps]), - ?assertMatch(#{<<"user_id">> := _}, CreatedUser) - end, - ValidUsers), + fun(User) -> + {ok, 201, UserData} = request(post, UsersUri, User), + CreatedUser = jiffy:decode(UserData, [return_maps]), + ?assertMatch(#{<<"user_id">> := _}, CreatedUser) + end, + ValidUsers + ), {ok, 200, Page1Data} = request(get, UsersUri ++ "?page=1&limit=2"), - #{<<"data">> := Page1Users, - <<"meta">> := - #{<<"page">> := 1, - <<"limit">> := 2, - <<"count">> := 3}} = - jiffy:decode(Page1Data, [return_maps]), + #{ + <<"data">> := Page1Users, + <<"meta">> := + #{ + <<"page">> := 1, + <<"limit">> := 2, + <<"count">> := 3 + } + } = + jiffy:decode(Page1Data, [return_maps]), {ok, 200, Page2Data} = request(get, UsersUri ++ "?page=2&limit=2"), - #{<<"data">> := Page2Users, - <<"meta">> := - #{<<"page">> := 2, - <<"limit">> := 2, - <<"count">> := 3}} = jiffy:decode(Page2Data, [return_maps]), + #{ + <<"data">> := Page2Users, + <<"meta">> := + #{ + <<"page">> := 2, + <<"limit">> := 2, + <<"count">> := 3 + } + } = jiffy:decode(Page2Data, [return_maps]), ?assertEqual(2, length(Page1Users)), ?assertEqual(1, length(Page2Users)), ?assertEqual( - [<<"u1">>, <<"u2">>, <<"u3">>], - lists:usort([ UserId || #{<<"user_id">> := UserId} <- Page1Users ++ Page2Users])). + [<<"u1">>, <<"u2">>, <<"u3">>], + lists:usort([UserId || #{<<"user_id">> := UserId} <- Page1Users ++ Page2Users]) + ). test_authenticator_user(PathPrefix) -> UsersUri = uri(PathPrefix ++ [?CONF_NS, "password_based:built_in_database", "users"]), {ok, 200, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS]), - emqx_authn_test_lib:built_in_database_example()), + post, + uri(PathPrefix ++ [?CONF_NS]), + emqx_authn_test_lib:built_in_database_example() + ), User = #{user_id => <<"u1">>, password => <<"p1">>}, {ok, 201, _} = request(post, UsersUri, User), @@ -299,141 +340,161 @@ test_authenticator_user(PathPrefix) -> ?assertNotMatch(#{<<"password">> := _}, FetchedUser), ValidUserUpdates = [ - #{password => <<"p1">>}, - #{password => <<"p1">>, is_superuser => true}], + #{password => <<"p1">>}, + #{password => <<"p1">>, is_superuser => true} + ], lists:foreach( - fun(UserUpdate) -> {ok, 200, _} = request(put, UsersUri ++ "/u1", UserUpdate) end, - ValidUserUpdates), + fun(UserUpdate) -> {ok, 200, _} = request(put, UsersUri ++ "/u1", UserUpdate) end, + ValidUserUpdates + ), InvalidUserUpdates = [#{user_id => <<"u1">>, password => <<"p1">>}], lists:foreach( - fun(UserUpdate) -> {ok, 400, _} = request(put, UsersUri ++ "/u1", UserUpdate) end, - InvalidUserUpdates), + fun(UserUpdate) -> {ok, 400, _} = request(put, UsersUri ++ "/u1", UserUpdate) end, + InvalidUserUpdates + ), {ok, 404, _} = request(delete, UsersUri ++ "/u123"), {ok, 204, _} = request(delete, UsersUri ++ "/u1"). test_authenticator_move(PathPrefix) -> AuthenticatorConfs = [ - emqx_authn_test_lib:http_example(), - emqx_authn_test_lib:jwt_example(), - emqx_authn_test_lib:built_in_database_example() - ], + emqx_authn_test_lib:http_example(), + emqx_authn_test_lib:jwt_example(), + emqx_authn_test_lib:built_in_database_example() + ], lists:foreach( - fun(Conf) -> - {ok, 200, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS]), - Conf) - end, - AuthenticatorConfs), + fun(Conf) -> + {ok, 200, _} = request( + post, + uri(PathPrefix ++ [?CONF_NS]), + Conf + ) + end, + AuthenticatorConfs + ), ?assertAuthenticatorsMatch( - [ - #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}, - #{<<"mechanism">> := <<"jwt">>}, - #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>} - ], - PathPrefix ++ [?CONF_NS]), + [ + #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}, + #{<<"mechanism">> := <<"jwt">>}, + #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>} + ], + PathPrefix ++ [?CONF_NS] + ), %% Invalid moves {ok, 400, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), - #{position => <<"up">>}), + post, + uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), + #{position => <<"up">>} + ), {ok, 400, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), - #{}), + post, + uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), + #{} + ), {ok, 404, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), - #{position => <<"before:invalid">>}), + post, + uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), + #{position => <<"before:invalid">>} + ), {ok, 404, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), - #{position => <<"before:password_based:redis">>}), + post, + uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), + #{position => <<"before:password_based:redis">>} + ), {ok, 404, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), - #{position => <<"before:password_based:redis">>}), + post, + uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), + #{position => <<"before:password_based:redis">>} + ), %% Valid moves %% test front {ok, 204, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), - #{position => <<"front">>}), + post, + uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), + #{position => <<"front">>} + ), ?assertAuthenticatorsMatch( - [ - #{<<"mechanism">> := <<"jwt">>}, - #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}, - #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>} - ], - PathPrefix ++ [?CONF_NS]), + [ + #{<<"mechanism">> := <<"jwt">>}, + #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}, + #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>} + ], + PathPrefix ++ [?CONF_NS] + ), %% test rear {ok, 204, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), - #{position => <<"rear">>}), + post, + uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), + #{position => <<"rear">>} + ), ?assertAuthenticatorsMatch( - [ - #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}, - #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>}, - #{<<"mechanism">> := <<"jwt">>} - ], - PathPrefix ++ [?CONF_NS]), + [ + #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}, + #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>}, + #{<<"mechanism">> := <<"jwt">>} + ], + PathPrefix ++ [?CONF_NS] + ), %% test before {ok, 204, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), - #{position => <<"before:password_based:built_in_database">>}), + post, + uri(PathPrefix ++ [?CONF_NS, "jwt", "move"]), + #{position => <<"before:password_based:built_in_database">>} + ), ?assertAuthenticatorsMatch( - [ - #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}, - #{<<"mechanism">> := <<"jwt">>}, - #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>} - ], - PathPrefix ++ [?CONF_NS]), + [ + #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}, + #{<<"mechanism">> := <<"jwt">>}, + #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>} + ], + PathPrefix ++ [?CONF_NS] + ), %% test after {ok, 204, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS, "password_based%3Abuilt_in_database", "move"]), - #{position => <<"after:password_based:http">>}), + post, + uri(PathPrefix ++ [?CONF_NS, "password_based%3Abuilt_in_database", "move"]), + #{position => <<"after:password_based:http">>} + ), ?assertAuthenticatorsMatch( - [ - #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}, - #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>}, - #{<<"mechanism">> := <<"jwt">>} - ], - PathPrefix ++ [?CONF_NS]). + [ + #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"http">>}, + #{<<"mechanism">> := <<"password_based">>, <<"backend">> := <<"built_in_database">>}, + #{<<"mechanism">> := <<"jwt">>} + ], + PathPrefix ++ [?CONF_NS] + ). test_authenticator_import_users(PathPrefix) -> ImportUri = uri( - PathPrefix ++ - [?CONF_NS, "password_based:built_in_database", "import_users"]), - + PathPrefix ++ + [?CONF_NS, "password_based:built_in_database", "import_users"] + ), {ok, 200, _} = request( - post, - uri(PathPrefix ++ [?CONF_NS]), - emqx_authn_test_lib:built_in_database_example()), + post, + uri(PathPrefix ++ [?CONF_NS]), + emqx_authn_test_lib:built_in_database_example() + ), {ok, 400, _} = request(post, ImportUri, #{}), diff --git a/apps/emqx_authn/test/emqx_authn_http_SUITE.erl b/apps/emqx_authn/test/emqx_authn_http_SUITE.erl index c42aef019..2ccb0c9df 100644 --- a/apps/emqx_authn/test/emqx_authn_http_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_http_SUITE.erl @@ -28,12 +28,12 @@ -define(HTTP_PORT, 33333). -define(HTTP_PATH, "/auth"). --define(CREDENTIALS, #{username => <<"plain">>, - password => <<"plain">>, - listener => 'tcp:default', - protocol => mqtt - }). - +-define(CREDENTIALS, #{ + username => <<"plain">>, + password => <<"plain">>, + listener => 'tcp:default', + protocol => mqtt +}). all() -> emqx_common_test_helpers:all(?MODULE). @@ -46,8 +46,9 @@ init_per_suite(Config) -> end_per_suite(_) -> emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), emqx_common_test_helpers:stop_apps([emqx_authn]), application:stop(cowboy), ok. @@ -55,8 +56,9 @@ end_per_suite(_) -> init_per_testcase(_Case, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), {ok, _} = emqx_authn_http_test_server:start_link(?HTTP_PORT, ?HTTP_PATH), Config. @@ -71,8 +73,9 @@ t_create(_Config) -> AuthConfig = raw_http_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), {ok, [#{provider := emqx_authn_http}]} = emqx_authentication:list_authenticators(?GLOBAL). @@ -81,83 +84,96 @@ t_create_invalid(_Config) -> InvalidConfigs = [ - AuthConfig#{headers => []}, - AuthConfig#{method => delete} + AuthConfig#{headers => []}, + AuthConfig#{method => delete} ], lists:foreach( - fun(Config) -> - ct:pal("creating authenticator with invalid config: ~p", [Config]), - {error, _} = - try - emqx:update_config( + fun(Config) -> + ct:pal("creating authenticator with invalid config: ~p", [Config]), + {error, _} = + try + emqx:update_config( ?PATH, - {create_authenticator, ?GLOBAL, Config}) - catch - throw:Error -> - {error, Error} - end, - {ok, []} = emqx_authentication:list_authenticators(?GLOBAL) - end, - InvalidConfigs). + {create_authenticator, ?GLOBAL, Config} + ) + catch + throw:Error -> + {error, Error} + end, + {ok, []} = emqx_authentication:list_authenticators(?GLOBAL) + end, + InvalidConfigs + ). t_authenticate(_Config) -> ok = lists:foreach( - fun(Sample) -> - ct:pal("test_user_auth sample: ~p", [Sample]), - test_user_auth(Sample) - end, - samples()). + fun(Sample) -> + ct:pal("test_user_auth sample: ~p", [Sample]), + test_user_auth(Sample) + end, + samples() + ). -test_user_auth(#{handler := Handler, - config_params := SpecificConfgParams, - result := Result}) -> +test_user_auth(#{ + handler := Handler, + config_params := SpecificConfgParams, + result := Result +}) -> AuthConfig = maps:merge(raw_http_auth_config(), SpecificConfgParams), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), ok = emqx_authn_http_test_server:set_handler(Handler), ?assertEqual(Result, emqx_access_control:authenticate(?CREDENTIALS)), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL). + [authentication], + ?GLOBAL + ). t_destroy(_Config) -> AuthConfig = raw_http_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), ok = emqx_authn_http_test_server:set_handler( - fun(Req0, State) -> - Req = cowboy_req:reply(200, Req0), - {ok, Req, State} - end), + fun(Req0, State) -> + Req = cowboy_req:reply(200, Req0), + {ok, Req, State} + end + ), - {ok, [#{provider := emqx_authn_http, state := State}]} - = emqx_authentication:list_authenticators(?GLOBAL), + {ok, [#{provider := emqx_authn_http, state := State}]} = + emqx_authentication:list_authenticators(?GLOBAL), Credentials = maps:with([username, password], ?CREDENTIALS), {ok, _} = emqx_authn_http:authenticate( - Credentials, - State), + Credentials, + State + ), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), % Authenticator should not be usable anymore ?assertMatch( - ignore, - emqx_authn_http:authenticate( - Credentials, - State)). + ignore, + emqx_authn_http:authenticate( + Credentials, + State + ) + ). t_update(_Config) -> CorrectConfig = raw_http_auth_config(), @@ -165,74 +181,80 @@ t_update(_Config) -> CorrectConfig#{url => <<"http://127.0.0.1:33333/invalid">>}, {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, IncorrectConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, IncorrectConfig} + ), ok = emqx_authn_http_test_server:set_handler( - fun(Req0, State) -> - Req = cowboy_req:reply(200, Req0), - {ok, Req, State} - end), + fun(Req0, State) -> + Req = cowboy_req:reply(200, Req0), + {ok, Req, State} + end + ), {error, not_authorized} = emqx_access_control:authenticate(?CREDENTIALS), % We update with config with correct query, provider should update and work properly {ok, _} = emqx:update_config( - ?PATH, - {update_authenticator, ?GLOBAL, <<"password_based:http">>, CorrectConfig}), + ?PATH, + {update_authenticator, ?GLOBAL, <<"password_based:http">>, CorrectConfig} + ), - {ok,_} = emqx_access_control:authenticate(?CREDENTIALS). + {ok, _} = emqx_access_control:authenticate(?CREDENTIALS). t_is_superuser(_Config) -> Config = raw_http_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, Config}), + ?PATH, + {create_authenticator, ?GLOBAL, Config} + ), Checks = [ - {json, <<"0">>, false}, - {json, <<"">>, false}, - {json, null, false}, - {json, 0, false}, + {json, <<"0">>, false}, + {json, <<"">>, false}, + {json, null, false}, + {json, 0, false}, - {json, <<"1">>, true}, - {json, <<"val">>, true}, - {json, 1, true}, - {json, 123, true}, + {json, <<"1">>, true}, + {json, <<"val">>, true}, + {json, 1, true}, + {json, 123, true}, - {form, <<"0">>, false}, - {form, <<"">>, false}, + {form, <<"0">>, false}, + {form, <<"">>, false}, - {form, <<"1">>, true}, - {form, <<"val">>, true} - ], + {form, <<"1">>, true}, + {form, <<"val">>, true} + ], lists:foreach(fun test_is_superuser/1, Checks). test_is_superuser({Kind, Value, ExpectedValue}) -> - - {ContentType, Res} = case Kind of - json -> - {<<"application/json">>, - jiffy:encode(#{is_superuser => Value})}; - form -> - {<<"application/x-www-form-urlencoded">>, - iolist_to_binary([<<"is_superuser=">>, Value])} - end, + {ContentType, Res} = + case Kind of + json -> + {<<"application/json">>, jiffy:encode(#{is_superuser => Value})}; + form -> + {<<"application/x-www-form-urlencoded">>, + iolist_to_binary([<<"is_superuser=">>, Value])} + end, ok = emqx_authn_http_test_server:set_handler( - fun(Req0, State) -> - Req = cowboy_req:reply( - 200, - #{<<"content-type">> => ContentType}, - Res, - Req0), - {ok, Req, State} - end), + fun(Req0, State) -> + Req = cowboy_req:reply( + 200, + #{<<"content-type">> => ContentType}, + Res, + Req0 + ), + {ok, Req, State} + end + ), ?assertMatch( - {ok, #{is_superuser := ExpectedValue}}, - emqx_access_control:authenticate(?CREDENTIALS)). + {ok, #{is_superuser := ExpectedValue}}, + emqx_access_control:authenticate(?CREDENTIALS) + ). %%------------------------------------------------------------------------------ %% Helpers @@ -252,138 +274,159 @@ raw_http_auth_config() -> samples() -> [ - %% simple get request - #{handler => fun(Req0, State) -> - #{username := <<"plain">>, - password := <<"plain">> - } = cowboy_req:match_qs([username, password], Req0), + %% simple get request + #{ + handler => fun(Req0, State) -> + #{ + username := <<"plain">>, + password := <<"plain">> + } = cowboy_req:match_qs([username, password], Req0), - Req = cowboy_req:reply(200, Req0), - {ok, Req, State} - end, - config_params => #{}, - result => {ok,#{is_superuser => false}} - }, + Req = cowboy_req:reply(200, Req0), + {ok, Req, State} + end, + config_params => #{}, + result => {ok, #{is_superuser => false}} + }, - %% get request with json body response - #{handler => fun(Req0, State) -> - Req = cowboy_req:reply( - 200, - #{<<"content-type">> => <<"application/json">>}, - jiffy:encode(#{is_superuser => true}), - Req0), - {ok, Req, State} - end, - config_params => #{}, - result => {ok,#{is_superuser => true, user_property => #{}}} - }, + %% get request with json body response + #{ + handler => fun(Req0, State) -> + Req = cowboy_req:reply( + 200, + #{<<"content-type">> => <<"application/json">>}, + jiffy:encode(#{is_superuser => true}), + Req0 + ), + {ok, Req, State} + end, + config_params => #{}, + result => {ok, #{is_superuser => true, user_property => #{}}} + }, - %% get request with url-form-encoded body response - #{handler => fun(Req0, State) -> - Req = cowboy_req:reply( - 200, - #{<<"content-type">> => - <<"application/x-www-form-urlencoded">>}, - <<"is_superuser=true">>, - Req0), - {ok, Req, State} - end, - config_params => #{}, - result => {ok,#{is_superuser => true, user_property => #{}}} - }, + %% get request with url-form-encoded body response + #{ + handler => fun(Req0, State) -> + Req = cowboy_req:reply( + 200, + #{ + <<"content-type">> => + <<"application/x-www-form-urlencoded">> + }, + <<"is_superuser=true">>, + Req0 + ), + {ok, Req, State} + end, + config_params => #{}, + result => {ok, #{is_superuser => true, user_property => #{}}} + }, - %% get request with response of unknown encoding - #{handler => fun(Req0, State) -> - Req = cowboy_req:reply( - 200, - #{<<"content-type">> => - <<"test/plain">>}, - <<"is_superuser=true">>, - Req0), - {ok, Req, State} - end, - config_params => #{}, - result => {ok,#{is_superuser => false}} - }, + %% get request with response of unknown encoding + #{ + handler => fun(Req0, State) -> + Req = cowboy_req:reply( + 200, + #{ + <<"content-type">> => + <<"test/plain">> + }, + <<"is_superuser=true">>, + Req0 + ), + {ok, Req, State} + end, + config_params => #{}, + result => {ok, #{is_superuser => false}} + }, - %% simple post request, application/json - #{handler => fun(Req0, State) -> - {ok, RawBody, Req1} = cowboy_req:read_body(Req0), - #{<<"username">> := <<"plain">>, - <<"password">> := <<"plain">> - } = jiffy:decode(RawBody, [return_maps]), - Req = cowboy_req:reply(200, Req1), - {ok, Req, State} - end, - config_params => #{ - method => post, - headers => #{<<"content-type">> => <<"application/json">>} - }, - result => {ok,#{is_superuser => false}} - }, + %% simple post request, application/json + #{ + handler => fun(Req0, State) -> + {ok, RawBody, Req1} = cowboy_req:read_body(Req0), + #{ + <<"username">> := <<"plain">>, + <<"password">> := <<"plain">> + } = jiffy:decode(RawBody, [return_maps]), + Req = cowboy_req:reply(200, Req1), + {ok, Req, State} + end, + config_params => #{ + method => post, + headers => #{<<"content-type">> => <<"application/json">>} + }, + result => {ok, #{is_superuser => false}} + }, - %% simple post request, application/x-www-form-urlencoded - #{handler => fun(Req0, State) -> - {ok, PostVars, Req1} = cowboy_req:read_urlencoded_body(Req0), - #{<<"username">> := <<"plain">>, - <<"password">> := <<"plain">> - } = maps:from_list(PostVars), - Req = cowboy_req:reply(200, Req1), - {ok, Req, State} - end, - config_params => #{ - method => post, - headers => #{<<"content-type">> => - <<"application/x-www-form-urlencoded">>} - }, - result => {ok,#{is_superuser => false}} - } + %% simple post request, application/x-www-form-urlencoded + #{ + handler => fun(Req0, State) -> + {ok, PostVars, Req1} = cowboy_req:read_urlencoded_body(Req0), + #{ + <<"username">> := <<"plain">>, + <<"password">> := <<"plain">> + } = maps:from_list(PostVars), + Req = cowboy_req:reply(200, Req1), + {ok, Req, State} + end, + config_params => #{ + method => post, + headers => #{ + <<"content-type">> => + <<"application/x-www-form-urlencoded">> + } + }, + result => {ok, #{is_superuser => false}} + }#{ + %% 204 code + handler => fun(Req0, State) -> + Req = cowboy_req:reply(204, Req0), + {ok, Req, State} + end, + config_params => #{}, + result => {ok, #{is_superuser => false}} + }, - %% 204 code - #{handler => fun(Req0, State) -> - Req = cowboy_req:reply(204, Req0), - {ok, Req, State} - end, - config_params => #{}, - result => {ok,#{is_superuser => false}} - }, + %% custom headers + #{ + handler => fun(Req0, State) -> + <<"Test Value">> = cowboy_req:header(<<"x-test-header">>, Req0), + Req = cowboy_req:reply(200, Req0), + {ok, Req, State} + end, + config_params => #{}, + result => {ok, #{is_superuser => false}} + }, - %% custom headers - #{handler => fun(Req0, State) -> - <<"Test Value">> = cowboy_req:header(<<"x-test-header">>, Req0), - Req = cowboy_req:reply(200, Req0), - {ok, Req, State} - end, - config_params => #{}, - result => {ok,#{is_superuser => false}} - }, + %% 400 code + #{ + handler => fun(Req0, State) -> + Req = cowboy_req:reply(400, Req0), + {ok, Req, State} + end, + config_params => #{}, + result => {error, not_authorized} + }, - %% 400 code - #{handler => fun(Req0, State) -> - Req = cowboy_req:reply(400, Req0), - {ok, Req, State} - end, - config_params => #{}, - result => {error,not_authorized} - }, + %% 500 code + #{ + handler => fun(Req0, State) -> + Req = cowboy_req:reply(500, Req0), + {ok, Req, State} + end, + config_params => #{}, + result => {error, not_authorized} + }, - %% 500 code - #{handler => fun(Req0, State) -> - Req = cowboy_req:reply(500, Req0), - {ok, Req, State} - end, - config_params => #{}, - result => {error,not_authorized} - }, - - %% Handling error - #{handler => fun(Req0, State) -> - error(woops), - {ok, Req0, State} - end, - config_params => #{}, - result => {error,not_authorized} - } + %% Handling error + #{ + handler => fun(Req0, State) -> + error(woops), + {ok, Req0, State} + end, + config_params => #{}, + result => {error, not_authorized} + } ]. start_apps(Apps) -> diff --git a/apps/emqx_authn/test/emqx_authn_http_test_server.erl b/apps/emqx_authn/test/emqx_authn_http_test_server.erl index e3050a3ae..5f0b3e93b 100644 --- a/apps/emqx_authn/test/emqx_authn_http_test_server.erl +++ b/apps/emqx_authn/test/emqx_authn_http_test_server.erl @@ -26,11 +26,12 @@ -export([init/1]). % API --export([start_link/2, - start_link/3, - stop/0, - set_handler/1 - ]). +-export([ + start_link/2, + start_link/3, + stop/0, + set_handler/1 +]). %%------------------------------------------------------------------------------ %% API @@ -55,10 +56,11 @@ set_handler(F) when is_function(F, 2) -> init([Port, Path, SSLOpts]) -> Dispatch = cowboy_router:compile( - [ - {'_', [{Path, ?MODULE, []}]} - ]), - + [ + {'_', [{Path, ?MODULE, []}]} + ] + ), + ProtoOpts = #{env => #{dispatch => Dispatch}}, Tab = ets:new(?MODULE, [set, named_table, public]), @@ -83,23 +85,28 @@ init(Req, State) -> %%------------------------------------------------------------------------------ transport_settings(Port, false) -> - TransOpts = #{socket_opts => [{port, Port}], - connection_type => supervisor}, + TransOpts = #{ + socket_opts => [{port, Port}], + connection_type => supervisor + }, {ranch_tcp, TransOpts, cowboy_clear}; - transport_settings(Port, SSLOpts) -> - TransOpts = #{socket_opts => [{port, Port}, - {next_protocols_advertised, [<<"h2">>, <<"http/1.1">>]}, - {alpn_preferred_protocols, [<<"h2">>, <<"http/1.1">>]} - | SSLOpts], - connection_type => supervisor}, + TransOpts = #{ + socket_opts => [ + {port, Port}, + {next_protocols_advertised, [<<"h2">>, <<"http/1.1">>]}, + {alpn_preferred_protocols, [<<"h2">>, <<"http/1.1">>]} + | SSLOpts + ], + connection_type => supervisor + }, {ranch_ssl, TransOpts, cowboy_tls}. default_handler(Req0, State) -> Req = cowboy_req:reply( - 400, - #{<<"content-type">> => <<"text/plain">>}, - <<"">>, - Req0), + 400, + #{<<"content-type">> => <<"text/plain">>}, + <<"">>, + Req0 + ), {ok, Req, State}. - diff --git a/apps/emqx_authn/test/emqx_authn_https_SUITE.erl b/apps/emqx_authn/test/emqx_authn_https_SUITE.erl index 481b5477c..b67f040ea 100644 --- a/apps/emqx_authn/test/emqx_authn_https_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_https_SUITE.erl @@ -28,12 +28,12 @@ -define(HTTPS_PORT, 33333). -define(HTTPS_PATH, "/auth"). --define(CREDENTIALS, #{username => <<"plain">>, - password => <<"plain">>, - listener => 'tcp:default', - protocol => mqtt - }). - +-define(CREDENTIALS, #{ + username => <<"plain">>, + password => <<"plain">>, + listener => 'tcp:default', + protocol => mqtt +}). all() -> emqx_common_test_helpers:all(?MODULE). @@ -46,8 +46,9 @@ init_per_suite(Config) -> end_per_suite(_) -> emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), emqx_common_test_helpers:stop_apps([emqx_authn]), application:stop(cowboy), ok. @@ -55,8 +56,9 @@ end_per_suite(_) -> init_per_testcase(_Case, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), {ok, _} = emqx_authn_http_test_server:start_link(?HTTPS_PORT, ?HTTPS_PATH, server_ssl_opts()), ok = emqx_authn_http_test_server:set_handler(fun cowboy_handler/2), Config. @@ -70,46 +72,62 @@ end_per_testcase(_Case, _Config) -> t_create(_Config) -> {ok, _} = create_https_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.2">>], - <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>]}), + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>] + } + ), ?assertMatch( - {ok, _}, - emqx_access_control:authenticate(?CREDENTIALS)). + {ok, _}, + emqx_access_control:authenticate(?CREDENTIALS) + ). t_create_invalid_domain(_Config) -> {ok, _} = create_https_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server-unknown-host">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.2">>], - <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>]}), + #{ + <<"server_name_indication">> => <<"authn-server-unknown-host">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>] + } + ), ?assertEqual( - {error, not_authorized}, - emqx_access_control:authenticate(?CREDENTIALS)). + {error, not_authorized}, + emqx_access_control:authenticate(?CREDENTIALS) + ). t_create_invalid_version(_Config) -> {ok, _} = create_https_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.1">>]}), + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.1">>] + } + ), ?assertEqual( - {error, not_authorized}, - emqx_access_control:authenticate(?CREDENTIALS)). + {error, not_authorized}, + emqx_access_control:authenticate(?CREDENTIALS) + ). t_create_invalid_ciphers(_Config) -> {ok, _} = create_https_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.2">>], - <<"ciphers">> => [<<"ECDHE-ECDSA-AES256-SHA384">>]}), + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-ECDSA-AES256-SHA384">>] + } + ), ?assertEqual( - {error, not_authorized}, - emqx_access_control:authenticate(?CREDENTIALS)). + {error, not_authorized}, + emqx_access_control:authenticate(?CREDENTIALS) + ). %%------------------------------------------------------------------------------ %% Helpers @@ -121,8 +139,9 @@ create_https_auth_with_ssl_opts(SpecificSSLOpts) -> raw_https_auth_config(SpecificSSLOpts) -> SSLOpts = maps:merge( - emqx_authn_test_lib:client_ssl_cert_opts(), - #{enable => <<"true">>}), + emqx_authn_test_lib:client_ssl_cert_opts(), + #{enable => <<"true">>} + ), #{ mechanism => <<"password_based">>, enable => <<"true">>, @@ -133,7 +152,7 @@ raw_https_auth_config(SpecificSSLOpts) -> body => #{<<"username">> => ?PH_USERNAME, <<"password">> => ?PH_PASSWORD}, headers => #{<<"X-Test-Header">> => <<"Test Value">>}, ssl => maps:merge(SSLOpts, SpecificSSLOpts) - }. + }. start_apps(Apps) -> lists:foreach(fun application:ensure_all_started/1, Apps). @@ -147,15 +166,17 @@ cert_path(FileName) -> cowboy_handler(Req0, State) -> Req = cowboy_req:reply( - 200, - Req0), + 200, + Req0 + ), {ok, Req, State}. server_ssl_opts() -> - [{keyfile, cert_path("server.key")}, - {certfile, cert_path("server.crt")}, - {cacertfile, cert_path("ca.crt")}, - {verify, verify_none}, - {versions, ['tlsv1.2', 'tlsv1.3']}, - {ciphers, ["ECDHE-RSA-AES256-GCM-SHA384", "TLS_CHACHA20_POLY1305_SHA256"]} + [ + {keyfile, cert_path("server.key")}, + {certfile, cert_path("server.crt")}, + {cacertfile, cert_path("ca.crt")}, + {verify, verify_none}, + {versions, ['tlsv1.2', 'tlsv1.3']}, + {ciphers, ["ECDHE-RSA-AES256-GCM-SHA384", "TLS_CHACHA20_POLY1305_SHA256"]} ]. diff --git a/apps/emqx_authn/test/emqx_authn_jwt_SUITE.erl b/apps/emqx_authn/test/emqx_authn_jwt_SUITE.erl index ae62530e3..a7b1d3f55 100644 --- a/apps/emqx_authn/test/emqx_authn_jwt_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_jwt_SUITE.erl @@ -28,7 +28,6 @@ -define(JWKS_PORT, 33333). -define(JWKS_PATH, "/jwks.json"). - all() -> emqx_common_test_helpers:all(?MODULE). @@ -51,24 +50,30 @@ end_per_suite(_) -> t_jwt_authenticator_hmac_based(_) -> Secret = <<"abcdef">>, - Config = #{mechanism => jwt, - use_jwks => false, - algorithm => 'hmac-based', - secret => Secret, - secret_base64_encoded => false, - verify_claims => []}, + Config = #{ + mechanism => jwt, + use_jwks => false, + algorithm => 'hmac-based', + secret => Secret, + secret_base64_encoded => false, + verify_claims => [] + }, {ok, State} = emqx_authn_jwt:create(?AUTHN_ID, Config), Payload = #{<<"username">> => <<"myuser">>}, JWS = generate_jws('hmac-based', Payload, Secret), - Credential = #{username => <<"myuser">>, - password => JWS}, + Credential = #{ + username => <<"myuser">>, + password => JWS + }, ?assertEqual({ok, #{is_superuser => false}}, emqx_authn_jwt:authenticate(Credential, State)), Payload1 = #{<<"username">> => <<"myuser">>, <<"is_superuser">> => true}, JWS1 = generate_jws('hmac-based', Payload1, Secret), - Credential1 = #{username => <<"myuser">>, - password => JWS1}, + Credential1 = #{ + username => <<"myuser">>, + password => JWS1 + }, ?assertEqual({ok, #{is_superuser => true}}, emqx_authn_jwt:authenticate(Credential1, State)), BadJWS = generate_jws('hmac-based', Payload, <<"bad_secret">>), @@ -76,59 +81,84 @@ t_jwt_authenticator_hmac_based(_) -> ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential2, State)), %% secret_base64_encoded - Config2 = Config#{secret => base64:encode(Secret), - secret_base64_encoded => true}, + Config2 = Config#{ + secret => base64:encode(Secret), + secret_base64_encoded => true + }, {ok, State2} = emqx_authn_jwt:update(Config2, State), ?assertEqual({ok, #{is_superuser => false}}, emqx_authn_jwt:authenticate(Credential, State2)), %% invalid secret - BadConfig = Config#{secret => <<"emqxsecret">>, - secret_base64_encoded => true}, + BadConfig = Config#{ + secret => <<"emqxsecret">>, + secret_base64_encoded => true + }, {error, {invalid_parameter, secret}} = emqx_authn_jwt:create(?AUTHN_ID, BadConfig), Config3 = Config#{verify_claims => [{<<"username">>, <<"${username}">>}]}, {ok, State3} = emqx_authn_jwt:update(Config3, State2), ?assertEqual({ok, #{is_superuser => false}}, emqx_authn_jwt:authenticate(Credential, State3)), - ?assertEqual({error, bad_username_or_password}, emqx_authn_jwt:authenticate(Credential#{username => <<"otheruser">>}, State3)), + ?assertEqual( + {error, bad_username_or_password}, + emqx_authn_jwt:authenticate(Credential#{username => <<"otheruser">>}, State3) + ), %% Expiration - Payload3 = #{ <<"username">> => <<"myuser">> - , <<"exp">> => erlang:system_time(second) - 60}, + Payload3 = #{ + <<"username">> => <<"myuser">>, + <<"exp">> => erlang:system_time(second) - 60 + }, JWS3 = generate_jws('hmac-based', Payload3, Secret), Credential3 = Credential#{password => JWS3}, - ?assertEqual({error, bad_username_or_password}, emqx_authn_jwt:authenticate(Credential3, State3)), + ?assertEqual( + {error, bad_username_or_password}, emqx_authn_jwt:authenticate(Credential3, State3) + ), - Payload4 = #{ <<"username">> => <<"myuser">> - , <<"exp">> => erlang:system_time(second) + 60}, + Payload4 = #{ + <<"username">> => <<"myuser">>, + <<"exp">> => erlang:system_time(second) + 60 + }, JWS4 = generate_jws('hmac-based', Payload4, Secret), Credential4 = Credential#{password => JWS4}, ?assertEqual({ok, #{is_superuser => false}}, emqx_authn_jwt:authenticate(Credential4, State3)), %% Issued At - Payload5 = #{ <<"username">> => <<"myuser">> - , <<"iat">> => erlang:system_time(second) - 60}, + Payload5 = #{ + <<"username">> => <<"myuser">>, + <<"iat">> => erlang:system_time(second) - 60 + }, JWS5 = generate_jws('hmac-based', Payload5, Secret), Credential5 = Credential#{password => JWS5}, ?assertEqual({ok, #{is_superuser => false}}, emqx_authn_jwt:authenticate(Credential5, State3)), - Payload6 = #{ <<"username">> => <<"myuser">> - , <<"iat">> => erlang:system_time(second) + 60}, + Payload6 = #{ + <<"username">> => <<"myuser">>, + <<"iat">> => erlang:system_time(second) + 60 + }, JWS6 = generate_jws('hmac-based', Payload6, Secret), Credential6 = Credential#{password => JWS6}, - ?assertEqual({error, bad_username_or_password}, emqx_authn_jwt:authenticate(Credential6, State3)), + ?assertEqual( + {error, bad_username_or_password}, emqx_authn_jwt:authenticate(Credential6, State3) + ), %% Not Before - Payload7 = #{ <<"username">> => <<"myuser">> - , <<"nbf">> => erlang:system_time(second) - 60}, + Payload7 = #{ + <<"username">> => <<"myuser">>, + <<"nbf">> => erlang:system_time(second) - 60 + }, JWS7 = generate_jws('hmac-based', Payload7, Secret), Credential7 = Credential6#{password => JWS7}, ?assertEqual({ok, #{is_superuser => false}}, emqx_authn_jwt:authenticate(Credential7, State3)), - Payload8 = #{ <<"username">> => <<"myuser">> - , <<"nbf">> => erlang:system_time(second) + 60}, + Payload8 = #{ + <<"username">> => <<"myuser">>, + <<"nbf">> => erlang:system_time(second) + 60 + }, JWS8 = generate_jws('hmac-based', Payload8, Secret), Credential8 = Credential#{password => JWS8}, - ?assertEqual({error, bad_username_or_password}, emqx_authn_jwt:authenticate(Credential8, State3)), + ?assertEqual( + {error, bad_username_or_password}, emqx_authn_jwt:authenticate(Credential8, State3) + ), ?assertEqual(ok, emqx_authn_jwt:destroy(State3)), ok. @@ -136,19 +166,25 @@ t_jwt_authenticator_hmac_based(_) -> t_jwt_authenticator_public_key(_) -> PublicKey = test_rsa_key(public), PrivateKey = test_rsa_key(private), - Config = #{mechanism => jwt, - use_jwks => false, - algorithm => 'public-key', - certificate => PublicKey, - verify_claims => []}, + Config = #{ + mechanism => jwt, + use_jwks => false, + algorithm => 'public-key', + certificate => PublicKey, + verify_claims => [] + }, {ok, State} = emqx_authn_jwt:create(?AUTHN_ID, Config), Payload = #{<<"username">> => <<"myuser">>}, JWS = generate_jws('public-key', Payload, PrivateKey), - Credential = #{username => <<"myuser">>, - password => JWS}, + Credential = #{ + username => <<"myuser">>, + password => JWS + }, ?assertEqual({ok, #{is_superuser => false}}, emqx_authn_jwt:authenticate(Credential, State)), - ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State)), + ?assertEqual( + ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State) + ), ?assertEqual(ok, emqx_authn_jwt:destroy(State)), ok. @@ -160,63 +196,77 @@ t_jwks_renewal(_Config) -> PrivateKey = test_rsa_key(private), Payload = #{<<"username">> => <<"myuser">>}, JWS = generate_jws('public-key', Payload, PrivateKey), - Credential = #{username => <<"myuser">>, - password => JWS}, - - BadConfig0 = #{mechanism => jwt, - algorithm => 'public-key', - ssl => #{enable => false}, - verify_claims => [], + Credential = #{ + username => <<"myuser">>, + password => JWS + }, - use_jwks => true, - endpoint => "https://127.0.0.1:" ++ integer_to_list(?JWKS_PORT + 1) ++ ?JWKS_PATH, - refresh_interval => 1000 - }, + BadConfig0 = #{ + mechanism => jwt, + algorithm => 'public-key', + ssl => #{enable => false}, + verify_claims => [], + + use_jwks => true, + endpoint => "https://127.0.0.1:" ++ integer_to_list(?JWKS_PORT + 1) ++ ?JWKS_PATH, + refresh_interval => 1000 + }, ok = snabbkaffe:start_trace(), {{ok, State0}, _} = ?wait_async_action( - emqx_authn_jwt:create(?AUTHN_ID, BadConfig0), - #{?snk_kind := jwks_endpoint_response}, - 10000), + emqx_authn_jwt:create(?AUTHN_ID, BadConfig0), + #{?snk_kind := jwks_endpoint_response}, + 10000 + ), ok = snabbkaffe:stop(), ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential, State0)), - ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State0)), + ?assertEqual( + ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State0) + ), ClientSSLOpts = client_ssl_opts(), BadClientSSLOpts = ClientSSLOpts#{server_name_indication => "authn-server-unknown-host"}, - BadConfig1 = BadConfig0#{endpoint => - "https://127.0.0.1:" ++ integer_to_list(?JWKS_PORT) ++ ?JWKS_PATH, - ssl => BadClientSSLOpts}, + BadConfig1 = BadConfig0#{ + endpoint => + "https://127.0.0.1:" ++ integer_to_list(?JWKS_PORT) ++ ?JWKS_PATH, + ssl => BadClientSSLOpts + }, ok = snabbkaffe:start_trace(), {{ok, State1}, _} = ?wait_async_action( - emqx_authn_jwt:create(?AUTHN_ID, BadConfig1), - #{?snk_kind := jwks_endpoint_response}, - 10000), + emqx_authn_jwt:create(?AUTHN_ID, BadConfig1), + #{?snk_kind := jwks_endpoint_response}, + 10000 + ), ok = snabbkaffe:stop(), ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential, State1)), - ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State0)), + ?assertEqual( + ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State0) + ), GoodConfig = BadConfig1#{ssl => ClientSSLOpts}, ok = snabbkaffe:start_trace(), {{ok, State2}, _} = ?wait_async_action( - emqx_authn_jwt:update(GoodConfig, State1), - #{?snk_kind := jwks_endpoint_response}, - 10000), + emqx_authn_jwt:update(GoodConfig, State1), + #{?snk_kind := jwks_endpoint_response}, + 10000 + ), ok = snabbkaffe:stop(), ?assertEqual({ok, #{is_superuser => false}}, emqx_authn_jwt:authenticate(Credential, State2)), - ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State2)), + ?assertEqual( + ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State2) + ), ?assertEqual(ok, emqx_authn_jwt:destroy(State2)), ok = emqx_authn_http_test_server:stop(). @@ -229,15 +279,15 @@ jwks_handler(Req0, State) -> JWK = jose_jwk:from_pem_file(test_rsa_key(public)), JWKS = jose_jwk_set:to_map([JWK], #{}), Req = cowboy_req:reply( - 200, - #{<<"content-type">> => <<"application/json">>}, - jiffy:encode(JWKS), - Req0), + 200, + #{<<"content-type">> => <<"application/json">>}, + jiffy:encode(JWKS), + Req0 + ), {ok, Req, State}. test_rsa_key(public) -> data_file("public_key.pem"); - test_rsa_key(private) -> data_file("private_key.pem"). @@ -250,32 +300,37 @@ cert_file(Name) -> generate_jws('hmac-based', Payload, Secret) -> JWK = jose_jwk:from_oct(Secret), - Header = #{ <<"alg">> => <<"HS256">> - , <<"typ">> => <<"JWT">> - }, + Header = #{ + <<"alg">> => <<"HS256">>, + <<"typ">> => <<"JWT">> + }, Signed = jose_jwt:sign(JWK, Header, Payload), {_, JWS} = jose_jws:compact(Signed), JWS; generate_jws('public-key', Payload, PrivateKey) -> JWK = jose_jwk:from_pem_file(PrivateKey), - Header = #{ <<"alg">> => <<"RS256">> - , <<"typ">> => <<"JWT">> - }, + Header = #{ + <<"alg">> => <<"RS256">>, + <<"typ">> => <<"JWT">> + }, Signed = jose_jwt:sign(JWK, Header, Payload), {_, JWS} = jose_jws:compact(Signed), JWS. client_ssl_opts() -> maps:merge( - emqx_authn_test_lib:client_ssl_cert_opts(), - #{enable => true, - verify => verify_peer, - server_name_indication => "authn-server" - }). + emqx_authn_test_lib:client_ssl_cert_opts(), + #{ + enable => true, + verify => verify_peer, + server_name_indication => "authn-server" + } + ). server_ssl_opts() -> - [{keyfile, cert_file("server.key")}, - {certfile, cert_file("server.crt")}, - {cacertfile, cert_file("ca.crt")}, - {verify, verify_none} + [ + {keyfile, cert_file("server.key")}, + {certfile, cert_file("server.crt")}, + {cacertfile, cert_file("ca.crt")}, + {verify, verify_none} ]. diff --git a/apps/emqx_authn/test/emqx_authn_mnesia_SUITE.erl b/apps/emqx_authn/test/emqx_authn_mnesia_SUITE.erl index ff380f850..0c3f1a9e8 100644 --- a/apps/emqx_authn/test/emqx_authn_mnesia_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_mnesia_SUITE.erl @@ -76,7 +76,8 @@ t_check_schema(_Config) -> ?assertException( throw, {emqx_authn_mnesia, _}, - hocon_tconf:check_plain(emqx_authn_mnesia, ?CONF(ConfigNotOk))). + hocon_tconf:check_plain(emqx_authn_mnesia, ?CONF(ConfigNotOk)) + ). t_create(_) -> Config0 = config(), @@ -110,7 +111,7 @@ t_destroy(_) -> ok = emqx_authn_mnesia:destroy(State0), {ok, State1} = emqx_authn_mnesia:create(?AUTHN_ID, Config), - {error,not_found} = emqx_authn_mnesia:lookup_user(<<"u">>, State1), + {error, not_found} = emqx_authn_mnesia:lookup_user(<<"u">>, State1), {ok, _} = emqx_authn_mnesia:lookup_user(<<"u">>, StateOther). t_authenticate(_) -> @@ -121,14 +122,17 @@ t_authenticate(_) -> {ok, _} = emqx_authn_mnesia:add_user(User, State), {ok, _} = emqx_authn_mnesia:authenticate( - #{username => <<"u">>, password => <<"p">>}, - State), + #{username => <<"u">>, password => <<"p">>}, + State + ), {error, bad_username_or_password} = emqx_authn_mnesia:authenticate( - #{username => <<"u">>, password => <<"badpass">>}, - State), + #{username => <<"u">>, password => <<"badpass">>}, + State + ), ignore = emqx_authn_mnesia:authenticate( - #{clientid => <<"u">>, password => <<"p">>}, - State). + #{clientid => <<"u">>, password => <<"p">>}, + State + ). t_add_user(_) -> Config = config(), @@ -157,16 +161,19 @@ t_update_user(_) -> {ok, _} = emqx_authn_mnesia:add_user(User, State), {error, not_found} = emqx_authn_mnesia:update_user(<<"u1">>, #{password => <<"p1">>}, State), - {ok, - #{user_id := <<"u">>, - is_superuser := true}} = emqx_authn_mnesia:update_user( - <<"u">>, - #{password => <<"p1">>, is_superuser => true}, - State), + {ok, #{ + user_id := <<"u">>, + is_superuser := true + }} = emqx_authn_mnesia:update_user( + <<"u">>, + #{password => <<"p1">>, is_superuser => true}, + State + ), {ok, _} = emqx_authn_mnesia:authenticate( - #{username => <<"u">>, password => <<"p1">>}, - State), + #{username => <<"u">>, password => <<"p1">>}, + State + ), {ok, #{is_superuser := true}} = emqx_authn_mnesia:lookup_user(<<"u">>, State). @@ -174,31 +181,47 @@ t_list_users(_) -> Config = config(), {ok, State} = emqx_authn_mnesia:create(?AUTHN_ID, Config), - Users = [#{user_id => <<"u1">>, password => <<"p">>}, - #{user_id => <<"u2">>, password => <<"p">>}, - #{user_id => <<"u3">>, password => <<"p">>}], + Users = [ + #{user_id => <<"u1">>, password => <<"p">>}, + #{user_id => <<"u2">>, password => <<"p">>}, + #{user_id => <<"u3">>, password => <<"p">>} + ], lists:foreach( - fun(U) -> {ok, _} = emqx_authn_mnesia:add_user(U, State) end, - Users), + fun(U) -> {ok, _} = emqx_authn_mnesia:add_user(U, State) end, + Users + ), - #{data := [#{is_superuser := false,user_id := _}, - #{is_superuser := false,user_id := _}], - meta := #{page := 1, limit := 2, count := 3}} = emqx_authn_mnesia:list_users( - #{<<"page">> => 1, <<"limit">> => 2}, - State), + #{ + data := [ + #{is_superuser := false, user_id := _}, + #{is_superuser := false, user_id := _} + ], + meta := #{page := 1, limit := 2, count := 3} + } = emqx_authn_mnesia:list_users( + #{<<"page">> => 1, <<"limit">> => 2}, + State + ), - #{data := [#{is_superuser := false,user_id := _}], - meta := #{page := 2, limit := 2, count := 3}} = emqx_authn_mnesia:list_users( - #{<<"page">> => 2, <<"limit">> => 2}, - State), + #{ + data := [#{is_superuser := false, user_id := _}], + meta := #{page := 2, limit := 2, count := 3} + } = emqx_authn_mnesia:list_users( + #{<<"page">> => 2, <<"limit">> => 2}, + State + ), - #{data := [#{is_superuser := false,user_id := <<"u3">>}], - meta := #{page := 1, limit := 20, count := 1}} = emqx_authn_mnesia:list_users( - #{ <<"page">> => 1 - , <<"limit">> => 20 - , <<"like_username">> => <<"3">>}, - State). + #{ + data := [#{is_superuser := false, user_id := <<"u3">>}], + meta := #{page := 1, limit := 20, count := 1} + } = emqx_authn_mnesia:list_users( + #{ + <<"page">> => 1, + <<"limit">> => 20, + <<"like_username">> => <<"3">> + }, + State + ). t_import_users(_) -> Config0 = config(), @@ -206,36 +229,44 @@ t_import_users(_) -> {ok, State} = emqx_authn_mnesia:create(?AUTHN_ID, Config), ok = emqx_authn_mnesia:import_users( - data_filename(<<"user-credentials.json">>), - State), + data_filename(<<"user-credentials.json">>), + State + ), ok = emqx_authn_mnesia:import_users( - data_filename(<<"user-credentials.csv">>), - State), + data_filename(<<"user-credentials.csv">>), + State + ), {error, {unsupported_file_format, _}} = emqx_authn_mnesia:import_users( - <<"/file/with/unknown.extension">>, - State), + <<"/file/with/unknown.extension">>, + State + ), {error, unknown_file_format} = emqx_authn_mnesia:import_users( - <<"/file/with/no/extension">>, - State), + <<"/file/with/no/extension">>, + State + ), {error, enoent} = emqx_authn_mnesia:import_users( - <<"/file/that/not/exist.json">>, - State), + <<"/file/that/not/exist.json">>, + State + ), {error, bad_format} = emqx_authn_mnesia:import_users( - data_filename(<<"user-credentials-malformed-0.json">>), - State), + data_filename(<<"user-credentials-malformed-0.json">>), + State + ), {error, {_, invalid_json}} = emqx_authn_mnesia:import_users( - data_filename(<<"user-credentials-malformed-1.json">>), - State), + data_filename(<<"user-credentials-malformed-1.json">>), + State + ), {error, bad_format} = emqx_authn_mnesia:import_users( - data_filename(<<"user-credentials-malformed.csv">>), - State). + data_filename(<<"user-credentials-malformed.csv">>), + State + ). %%------------------------------------------------------------------------------ %% Helpers @@ -246,7 +277,10 @@ data_filename(Name) -> filename:join([Dir, <<"data">>, Name]). config() -> - #{user_id_type => username, - password_hash_algorithm => #{name => bcrypt, - salt_rounds => 8} - }. + #{ + user_id_type => username, + password_hash_algorithm => #{ + name => bcrypt, + salt_rounds => 8 + } + }. diff --git a/apps/emqx_authn/test/emqx_authn_mongo_SUITE.erl b/apps/emqx_authn/test/emqx_authn_mongo_SUITE.erl index 13e39dcd8..ffd3dfd39 100644 --- a/apps/emqx_authn/test/emqx_authn_mongo_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_mongo_SUITE.erl @@ -24,7 +24,6 @@ -include_lib("eunit/include/eunit.hrl"). -include_lib("common_test/include/ct.hrl"). - -define(MONGO_HOST, "mongo"). -define(MONGO_CLIENT, 'emqx_authn_mongo_SUITE_client'). @@ -37,8 +36,9 @@ init_per_testcase(_TestCase, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), emqx_authentication:initialize_authentication(?GLOBAL, []), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), {ok, _} = mc_worker_api:connect(mongo_config()), Config. @@ -58,8 +58,9 @@ init_per_suite(Config) -> end_per_suite(_Config) -> emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), ok = stop_apps([emqx_resource, emqx_connector]), ok = emqx_common_test_helpers:stop_apps([emqx_authn]). @@ -71,8 +72,9 @@ t_create(_Config) -> AuthConfig = raw_mongo_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), {ok, [#{provider := emqx_authn_mongodb}]} = emqx_authentication:list_authenticators(?GLOBAL). @@ -81,79 +83,93 @@ t_create_invalid(_Config) -> InvalidConfigs = [ - AuthConfig#{mongo_type => <<"unknown">>}, - AuthConfig#{selector => <<"{ \"username\": \"${username}\" }">>}, - AuthConfig#{w_mode => <<"unknown">>} + AuthConfig#{mongo_type => <<"unknown">>}, + AuthConfig#{selector => <<"{ \"username\": \"${username}\" }">>}, + AuthConfig#{w_mode => <<"unknown">>} ], lists:foreach( - fun(Config) -> - {error, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, Config}), + fun(Config) -> + {error, _} = emqx:update_config( + ?PATH, + {create_authenticator, ?GLOBAL, Config} + ), - {ok, []} = emqx_authentication:list_authenticators(?GLOBAL) - end, - InvalidConfigs). + {ok, []} = emqx_authentication:list_authenticators(?GLOBAL) + end, + InvalidConfigs + ). t_authenticate(_Config) -> ok = init_seeds(), ok = lists:foreach( - fun(Sample) -> - ct:pal("test_user_auth sample: ~p", [Sample]), - test_user_auth(Sample) - end, - user_seeds()), + fun(Sample) -> + ct:pal("test_user_auth sample: ~p", [Sample]), + test_user_auth(Sample) + end, + user_seeds() + ), ok = drop_seeds(). -test_user_auth(#{credentials := Credentials0, - config_params := SpecificConfigParams, - result := Result}) -> +test_user_auth(#{ + credentials := Credentials0, + config_params := SpecificConfigParams, + result := Result +}) -> AuthConfig = maps:merge(raw_mongo_auth_config(), SpecificConfigParams), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), Credentials = Credentials0#{ - listener => 'tcp:default', - protocol => mqtt - }, + listener => 'tcp:default', + protocol => mqtt + }, ?assertEqual(Result, emqx_access_control:authenticate(Credentials)), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL). + [authentication], + ?GLOBAL + ). t_destroy(_Config) -> ok = init_seeds(), AuthConfig = raw_mongo_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), - {ok, [#{provider := emqx_authn_mongodb, state := State}]} - = emqx_authentication:list_authenticators(?GLOBAL), + {ok, [#{provider := emqx_authn_mongodb, state := State}]} = + emqx_authentication:list_authenticators(?GLOBAL), {ok, _} = emqx_authn_mongodb:authenticate( - #{username => <<"plain">>, - password => <<"plain">> - }, - State), + #{ + username => <<"plain">>, + password => <<"plain">> + }, + State + ), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), % Authenticator should not be usable anymore ?assertMatch( - ignore, - emqx_authn_mongodb:authenticate( - #{username => <<"plain">>, - password => <<"plain">> - }, - State)), + ignore, + emqx_authn_mongodb:authenticate( + #{ + username => <<"plain">>, + password => <<"plain">> + }, + State + ) + ), ok = drop_seeds(). @@ -164,48 +180,55 @@ t_update(_Config) -> CorrectConfig#{selector => #{<<"wrongfield">> => <<"wrongvalue">>}}, {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, IncorrectConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, IncorrectConfig} + ), {error, not_authorized} = emqx_access_control:authenticate( - #{username => <<"plain">>, - password => <<"plain">>, - listener => 'tcp:default', - protocol => mqtt - }), + #{ + username => <<"plain">>, + password => <<"plain">>, + listener => 'tcp:default', + protocol => mqtt + } + ), % We update with config with correct selector, provider should update and work properly {ok, _} = emqx:update_config( - ?PATH, - {update_authenticator, ?GLOBAL, <<"password_based:mongodb">>, CorrectConfig}), + ?PATH, + {update_authenticator, ?GLOBAL, <<"password_based:mongodb">>, CorrectConfig} + ), - {ok,_} = emqx_access_control:authenticate( - #{username => <<"plain">>, - password => <<"plain">>, - listener => 'tcp:default', - protocol => mqtt - }), + {ok, _} = emqx_access_control:authenticate( + #{ + username => <<"plain">>, + password => <<"plain">>, + listener => 'tcp:default', + protocol => mqtt + } + ), ok = drop_seeds(). t_is_superuser(_Config) -> Config = raw_mongo_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, Config}), + ?PATH, + {create_authenticator, ?GLOBAL, Config} + ), Checks = [ - {<<"0">>, false}, - {<<"">>, false}, - {null, false}, - {false, false}, - {0, false}, + {<<"0">>, false}, + {<<"">>, false}, + {null, false}, + {false, false}, + {0, false}, - {<<"1">>, true}, - {<<"val">>, true}, - {1, true}, - {123, true}, - {true, true} - ], + {<<"1">>, true}, + {<<"val">>, true}, + {1, true}, + {123, true}, + {true, true} + ], lists:foreach(fun test_is_superuser/1, Checks). @@ -213,24 +236,25 @@ test_is_superuser({Value, ExpectedValue}) -> {true, _} = mc_worker_api:delete(?MONGO_CLIENT, <<"users">>, #{}), UserData = #{ - username => <<"user">>, - password_hash => <<"plainsalt">>, - salt => <<"salt">>, - is_superuser => Value - }, + username => <<"user">>, + password_hash => <<"plainsalt">>, + salt => <<"salt">>, + is_superuser => Value + }, {{true, _}, _} = mc_worker_api:insert(?MONGO_CLIENT, <<"users">>, [UserData]), Credentials = #{ - listener => 'tcp:default', - protocol => mqtt, - username => <<"user">>, - password => <<"plain">> - }, + listener => 'tcp:default', + protocol => mqtt, + username => <<"user">>, + password => <<"plain">> + }, ?assertEqual( - {ok, #{is_superuser => ExpectedValue}}, - emqx_access_control:authenticate(Credentials)). + {ok, #{is_superuser => ExpectedValue}}, + emqx_access_control:authenticate(Credentials) + ). %%------------------------------------------------------------------------------ %% Helpers @@ -238,146 +262,160 @@ test_is_superuser({Value, ExpectedValue}) -> raw_mongo_auth_config() -> #{ - mechanism => <<"password_based">>, - password_hash_algorithm => #{name => <<"plain">>, - salt_position => <<"suffix">>}, - enable => <<"true">>, + mechanism => <<"password_based">>, + password_hash_algorithm => #{ + name => <<"plain">>, + salt_position => <<"suffix">> + }, + enable => <<"true">>, - backend => <<"mongodb">>, - mongo_type => <<"single">>, - database => <<"mqtt">>, - collection => <<"users">>, - server => mongo_server(), - w_mode => <<"unsafe">>, + backend => <<"mongodb">>, + mongo_type => <<"single">>, + database => <<"mqtt">>, + collection => <<"users">>, + server => mongo_server(), + w_mode => <<"unsafe">>, - selector => #{<<"username">> => <<"${username}">>}, - password_hash_field => <<"password_hash">>, - salt_field => <<"salt">>, - is_superuser_field => <<"is_superuser">> - }. + selector => #{<<"username">> => <<"${username}">>}, + password_hash_field => <<"password_hash">>, + salt_field => <<"salt">>, + is_superuser_field => <<"is_superuser">> + }. user_seeds() -> - [#{data => #{ - username => <<"plain">>, - password_hash => <<"plainsalt">>, - salt => <<"salt">>, - is_superuser => <<"1">> - }, - credentials => #{ - username => <<"plain">>, - password => <<"plain">> - }, - config_params => #{ - }, - result => {ok,#{is_superuser => true}} - }, - - #{data => #{ - username => <<"md5">>, - password_hash => <<"9b4d0c43d206d48279e69b9ad7132e22">>, - salt => <<"salt">>, - is_superuser => <<"0">> - }, - credentials => #{ - username => <<"md5">>, - password => <<"md5">> - }, - config_params => #{ - password_hash_algorithm => #{name => <<"md5">>, - salt_position => <<"suffix">> } - }, - result => {ok,#{is_superuser => false}} - }, - - #{data => #{ - username => <<"sha256">>, - password_hash => <<"ac63a624e7074776d677dd61a003b8c803eb11db004d0ec6ae032a5d7c9c5caf">>, - salt => <<"salt">>, - is_superuser => 1 + [ + #{ + data => #{ + username => <<"plain">>, + password_hash => <<"plainsalt">>, + salt => <<"salt">>, + is_superuser => <<"1">> + }, + credentials => #{ + username => <<"plain">>, + password => <<"plain">> + }, + config_params => #{}, + result => {ok, #{is_superuser => true}} }, - credentials => #{ - clientid => <<"sha256">>, - password => <<"sha256">> - }, - config_params => #{ - selector => #{<<"username">> => <<"${clientid}">>}, - password_hash_algorithm => #{name => <<"sha256">>, - salt_position => <<"prefix">>} - }, - result => {ok,#{is_superuser => true}} - }, - #{data => #{ - username => <<"bcrypt">>, - password_hash => - <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, - salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, - is_superuser => 0 - }, - credentials => #{ - username => <<"bcrypt">>, - password => <<"bcrypt">> - }, - config_params => #{ - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {ok,#{is_superuser => false}} - }, + #{ + data => #{ + username => <<"md5">>, + password_hash => <<"9b4d0c43d206d48279e69b9ad7132e22">>, + salt => <<"salt">>, + is_superuser => <<"0">> + }, + credentials => #{ + username => <<"md5">>, + password => <<"md5">> + }, + config_params => #{ + password_hash_algorithm => #{ + name => <<"md5">>, + salt_position => <<"suffix">> + } + }, + result => {ok, #{is_superuser => false}} + }, - #{data => #{ - username => <<"bcrypt0">>, - password_hash => - <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, - salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, - is_superuser => <<"0">> - }, - credentials => #{ - username => <<"bcrypt0">>, - password => <<"bcrypt">> - }, - config_params => #{ - % clientid variable & username credentials - selector => #{<<"username">> => <<"${clientid}">>}, - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {error,not_authorized} - }, + #{ + data => #{ + username => <<"sha256">>, + password_hash => + <<"ac63a624e7074776d677dd61a003b8c803eb11db004d0ec6ae032a5d7c9c5caf">>, + salt => <<"salt">>, + is_superuser => 1 + }, + credentials => #{ + clientid => <<"sha256">>, + password => <<"sha256">> + }, + config_params => #{ + selector => #{<<"username">> => <<"${clientid}">>}, + password_hash_algorithm => #{ + name => <<"sha256">>, + salt_position => <<"prefix">> + } + }, + result => {ok, #{is_superuser => true}} + }, - #{data => #{ - username => <<"bcrypt1">>, - password_hash => - <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, - salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, - is_superuser => <<"0">> - }, - credentials => #{ - username => <<"bcrypt1">>, - password => <<"bcrypt">> - }, - config_params => #{ - selector => #{<<"userid">> => <<"${clientid}">>}, - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {error,not_authorized} - }, + #{ + data => #{ + username => <<"bcrypt">>, + password_hash => + <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, + salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, + is_superuser => 0 + }, + credentials => #{ + username => <<"bcrypt">>, + password => <<"bcrypt">> + }, + config_params => #{ + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {ok, #{is_superuser => false}} + }, - #{data => #{ - username => <<"bcrypt2">>, - password_hash => - <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, - salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, - is_superuser => <<"0">> - }, - credentials => #{ - username => <<"bcrypt2">>, - % Wrong password - password => <<"wrongpass">> - }, - config_params => #{ - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {error,bad_username_or_password} - } + #{ + data => #{ + username => <<"bcrypt0">>, + password_hash => + <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, + salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, + is_superuser => <<"0">> + }, + credentials => #{ + username => <<"bcrypt0">>, + password => <<"bcrypt">> + }, + config_params => #{ + % clientid variable & username credentials + selector => #{<<"username">> => <<"${clientid}">>}, + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {error, not_authorized} + }, + + #{ + data => #{ + username => <<"bcrypt1">>, + password_hash => + <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, + salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, + is_superuser => <<"0">> + }, + credentials => #{ + username => <<"bcrypt1">>, + password => <<"bcrypt">> + }, + config_params => #{ + selector => #{<<"userid">> => <<"${clientid}">>}, + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {error, not_authorized} + }, + + #{ + data => #{ + username => <<"bcrypt2">>, + password_hash => + <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, + salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, + is_superuser => <<"0">> + }, + credentials => #{ + username => <<"bcrypt2">>, + % Wrong password + password => <<"wrongpass">> + }, + config_params => #{ + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {error, bad_username_or_password} + } ]. init_seeds() -> @@ -390,14 +428,14 @@ drop_seeds() -> ok. mongo_server() -> - iolist_to_binary(io_lib:format("~s",[?MONGO_HOST])). + iolist_to_binary(io_lib:format("~s", [?MONGO_HOST])). mongo_config() -> [ - {database, <<"mqtt">>}, - {host, ?MONGO_HOST}, - {port, ?MONGO_DEFAULT_PORT}, - {register, ?MONGO_CLIENT} + {database, <<"mqtt">>}, + {host, ?MONGO_HOST}, + {port, ?MONGO_DEFAULT_PORT}, + {register, ?MONGO_CLIENT} ]. start_apps(Apps) -> diff --git a/apps/emqx_authn/test/emqx_authn_mongo_tls_SUITE.erl b/apps/emqx_authn/test/emqx_authn_mongo_tls_SUITE.erl index 8c612bd20..48cee7509 100644 --- a/apps/emqx_authn/test/emqx_authn_mongo_tls_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_mongo_tls_SUITE.erl @@ -25,7 +25,6 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("snabbkaffe/include/snabbkaffe.hrl"). - -define(MONGO_HOST, "mongo-tls"). -define(PATH, [authentication]). @@ -37,8 +36,9 @@ init_per_testcase(_TestCase, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), emqx_authentication:initialize_authentication(?GLOBAL, []), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), Config. init_per_suite(Config) -> @@ -54,8 +54,9 @@ init_per_suite(Config) -> end_per_suite(_Config) -> emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), ok = stop_apps([emqx_resource, emqx_connector]), ok = emqx_common_test_helpers:stop_apps([emqx_authn]). @@ -72,69 +73,90 @@ end_per_suite(_Config) -> t_create(_Config) -> ?check_trace( - create_mongo_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.2">>], - <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>]}), - fun({ok, _}, Trace) -> + create_mongo_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>] + } + ), + fun({ok, _}, Trace) -> ?assertMatch( - [ok | _], - ?projection( - status, - ?of_kind(emqx_connector_mongo_health_check, Trace))) - end). - + [ok | _], + ?projection( + status, + ?of_kind(emqx_connector_mongo_health_check, Trace) + ) + ) + end + ). t_create_invalid_server_name(_Config) -> ?check_trace( - create_mongo_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server-unknown-host">>, - <<"verify">> => <<"verify_peer">>}), - fun(_, Trace) -> - ?assertNotEqual( - [ok], - ?projection( - status, - ?of_kind(emqx_connector_mongo_health_check, Trace))) - end). - + create_mongo_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server-unknown-host">>, + <<"verify">> => <<"verify_peer">> + } + ), + fun(_, Trace) -> + ?assertNotEqual( + [ok], + ?projection( + status, + ?of_kind(emqx_connector_mongo_health_check, Trace) + ) + ) + end + ). %% docker-compose-mongo-single-tls.yaml: %% --tlsDisabledProtocols TLS1_0,TLS1_1 t_create_invalid_version(_Config) -> ?check_trace( - create_mongo_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.1">>]}), - fun(_, Trace) -> - ?assertNotEqual( - [ok], - ?projection( - status, - ?of_kind(emqx_connector_mongo_health_check, Trace))) - end). - + create_mongo_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.1">>] + } + ), + fun(_, Trace) -> + ?assertNotEqual( + [ok], + ?projection( + status, + ?of_kind(emqx_connector_mongo_health_check, Trace) + ) + ) + end + ). %% docker-compose-mongo-single-tls.yaml: %% --setParameter opensslCipherConfig='HIGH:!EXPORT:!aNULL:!DHE:!kDHE@STRENGTH' t_invalid_ciphers(_Config) -> ?check_trace( - create_mongo_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.2">>], - <<"ciphers">> => [<<"DHE-RSA-AES256-GCM-SHA384">>]}), - fun(_, Trace) -> - ?assertNotEqual( - [ok], - ?projection( - status, - ?of_kind(emqx_connector_mongo_health_check, Trace))) - end). + create_mongo_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"DHE-RSA-AES256-GCM-SHA384">>] + } + ), + fun(_, Trace) -> + ?assertNotEqual( + [ok], + ?projection( + status, + ?of_kind(emqx_connector_mongo_health_check, Trace) + ) + ) + end + ). %%------------------------------------------------------------------------------ %% Helpers @@ -148,35 +170,38 @@ create_mongo_auth_with_ssl_opts(SpecificSSLOpts) -> raw_mongo_auth_config(SpecificSSLOpts) -> SSLOpts = maps:merge( - emqx_authn_test_lib:client_ssl_cert_opts(), - #{enable => <<"true">>}), + emqx_authn_test_lib:client_ssl_cert_opts(), + #{enable => <<"true">>} + ), #{ - mechanism => <<"password_based">>, - password_hash_algorithm => #{name => <<"plain">>, - salt_position => <<"suffix">>}, - enable => <<"true">>, + mechanism => <<"password_based">>, + password_hash_algorithm => #{ + name => <<"plain">>, + salt_position => <<"suffix">> + }, + enable => <<"true">>, - backend => <<"mongodb">>, - pool_size => 2, - mongo_type => <<"single">>, - database => <<"mqtt">>, - collection => <<"users">>, - server => mongo_server(), - w_mode => <<"unsafe">>, + backend => <<"mongodb">>, + pool_size => 2, + mongo_type => <<"single">>, + database => <<"mqtt">>, + collection => <<"users">>, + server => mongo_server(), + w_mode => <<"unsafe">>, - selector => #{<<"username">> => <<"${username}">>}, - password_hash_field => <<"password_hash">>, - salt_field => <<"salt">>, - is_superuser_field => <<"is_superuser">>, - topology => #{ - server_selection_timeout_ms => <<"10000ms">> - }, + selector => #{<<"username">> => <<"${username}">>}, + password_hash_field => <<"password_hash">>, + salt_field => <<"salt">>, + is_superuser_field => <<"is_superuser">>, + topology => #{ + server_selection_timeout_ms => <<"10000ms">> + }, - ssl => maps:merge(SSLOpts, SpecificSSLOpts) - }. + ssl => maps:merge(SSLOpts, SpecificSSLOpts) + }. mongo_server() -> - iolist_to_binary(io_lib:format("~s",[?MONGO_HOST])). + iolist_to_binary(io_lib:format("~s", [?MONGO_HOST])). start_apps(Apps) -> lists:foreach(fun application:ensure_all_started/1, Apps). diff --git a/apps/emqx_authn/test/emqx_authn_mqtt_test_client.erl b/apps/emqx_authn/test/emqx_authn_mqtt_test_client.erl index 162cf3f95..734a22cc7 100644 --- a/apps/emqx_authn/test/emqx_authn_mqtt_test_client.erl +++ b/apps/emqx_authn/test/emqx_authn_mqtt_test_client.erl @@ -21,28 +21,36 @@ -include_lib("emqx/include/emqx_mqtt.hrl"). %% API --export([start_link/2, - stop/1]). +-export([ + start_link/2, + stop/1 +]). -export([send/2]). %% gen_server callbacks --export([init/1, - handle_call/3, - handle_cast/2, - handle_info/2, - terminate/2]). +-export([ + init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2 +]). -define(TIMEOUT, 1000). --define(TCP_OPTIONS, [binary, {packet, raw}, {active, once}, - {nodelay, true}]). +-define(TCP_OPTIONS, [ + binary, + {packet, raw}, + {active, once}, + {nodelay, true} +]). --define(PARSE_OPTIONS, - #{strict_mode => false, - max_size => ?MAX_PACKET_SIZE, - version => ?MQTT_PROTO_V5 - }). +-define(PARSE_OPTIONS, #{ + strict_mode => false, + max_size => ?MAX_PACKET_SIZE, + version => ?MQTT_PROTO_V5 +}). %%-------------------------------------------------------------------- %% API @@ -63,26 +71,30 @@ send(Pid, Packet) -> init([Host, Port, Owner]) -> {ok, Socket} = gen_tcp:connect(Host, Port, ?TCP_OPTIONS, ?TIMEOUT), - {ok, #{owner => Owner, - socket => Socket, - parse_state => emqx_frame:initial_parse_state(?PARSE_OPTIONS) - }}. + {ok, #{ + owner => Owner, + socket => Socket, + parse_state => emqx_frame:initial_parse_state(?PARSE_OPTIONS) + }}. -handle_info({tcp, _Sock, Data}, #{parse_state := PSt, - owner := Owner, - socket := Socket} = St) -> +handle_info( + {tcp, _Sock, Data}, + #{ + parse_state := PSt, + owner := Owner, + socket := Socket + } = St +) -> {NewPSt, Packets} = process_incoming(PSt, Data, []), ok = deliver(Owner, Packets), ok = run_sock(Socket), {noreply, St#{parse_state => NewPSt}}; - handle_info({tcp_closed, _Sock}, St) -> {stop, normal, St}. handle_call({send, Packet}, _From, #{socket := Socket} = St) -> ok = gen_tcp:send(Socket, emqx_frame:serialize(Packet, ?MQTT_PROTO_V5)), {reply, ok, St}; - handle_call(stop, _From, #{socket := Socket} = St) -> ok = gen_tcp:close(Socket), {stop, normal, ok, St}. @@ -100,16 +112,16 @@ terminate(_Reason, _St) -> process_incoming(PSt, Data, Packets) -> case emqx_frame:parse(Data, PSt) of {more, NewPSt} -> - {NewPSt, lists:reverse(Packets)}; + {NewPSt, lists:reverse(Packets)}; {ok, Packet, Rest, NewPSt} -> process_incoming(NewPSt, Rest, [Packet | Packets]) end. -deliver(_Owner, []) -> ok; +deliver(_Owner, []) -> + ok; deliver(Owner, [Packet | Packets]) -> Owner ! {packet, Packet}, deliver(Owner, Packets). - run_sock(Socket) -> inet:setopts(Socket, [{active, once}]). diff --git a/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl b/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl index cb8933b40..3d2dba895 100644 --- a/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl @@ -40,8 +40,9 @@ init_per_testcase(_, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), emqx_authentication:initialize_authentication(?GLOBAL, []), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), Config. init_per_group(require_seeds, Config) -> @@ -59,11 +60,12 @@ init_per_suite(Config) -> ok = emqx_common_test_helpers:start_apps([emqx_authn]), ok = start_apps([emqx_resource, emqx_connector]), {ok, _} = emqx_resource:create_local( - ?MYSQL_RESOURCE, - ?RESOURCE_GROUP, - emqx_connector_mysql, - mysql_config(), - #{}), + ?MYSQL_RESOURCE, + ?RESOURCE_GROUP, + emqx_connector_mysql, + mysql_config(), + #{} + ), Config; false -> {skip, no_mysql} @@ -71,8 +73,9 @@ init_per_suite(Config) -> end_per_suite(_Config) -> emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), ok = emqx_resource:remove_local(?MYSQL_RESOURCE), ok = stop_apps([emqx_resource, emqx_connector]), ok = emqx_common_test_helpers:stop_apps([emqx_authn]). @@ -85,8 +88,9 @@ t_create(_Config) -> AuthConfig = raw_mysql_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), {ok, [#{provider := emqx_authn_mysql}]} = emqx_authentication:list_authenticators(?GLOBAL), emqx_authn_test_lib:delete_config(?ResourceID). @@ -96,108 +100,132 @@ t_create_invalid(_Config) -> InvalidConfigs = [ - maps:without([server], AuthConfig), - AuthConfig#{server => <<"unknownhost:3333">>}, - AuthConfig#{password => <<"wrongpass">>}, - AuthConfig#{database => <<"wrongdatabase">>} + maps:without([server], AuthConfig), + AuthConfig#{server => <<"unknownhost:3333">>}, + AuthConfig#{password => <<"wrongpass">>}, + AuthConfig#{database => <<"wrongdatabase">>} ], lists:foreach( - fun(Config) -> - {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, Config}), - emqx_authn_test_lib:delete_config(?ResourceID), - {ok, _} = emqx_authentication:list_authenticators(?GLOBAL) - end, - InvalidConfigs). + fun(Config) -> + {ok, _} = emqx:update_config( + ?PATH, + {create_authenticator, ?GLOBAL, Config} + ), + emqx_authn_test_lib:delete_config(?ResourceID), + {ok, _} = emqx_authentication:list_authenticators(?GLOBAL) + end, + InvalidConfigs + ). t_authenticate(_Config) -> ok = lists:foreach( - fun(Sample) -> - ct:pal("test_user_auth sample: ~p", [Sample]), - test_user_auth(Sample) - end, - user_seeds()). + fun(Sample) -> + ct:pal("test_user_auth sample: ~p", [Sample]), + test_user_auth(Sample) + end, + user_seeds() + ). -test_user_auth(#{credentials := Credentials0, - config_params := SpecificConfigParams, - result := Result}) -> +test_user_auth(#{ + credentials := Credentials0, + config_params := SpecificConfigParams, + result := Result +}) -> AuthConfig = maps:merge(raw_mysql_auth_config(), SpecificConfigParams), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), Credentials = Credentials0#{ - listener => 'tcp:default', - protocol => mqtt - }, + listener => 'tcp:default', + protocol => mqtt + }, ?assertEqual(Result, emqx_access_control:authenticate(Credentials)), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL). + [authentication], + ?GLOBAL + ). t_destroy(_Config) -> AuthConfig = raw_mysql_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), - {ok, [#{provider := emqx_authn_mysql, state := State}]} - = emqx_authentication:list_authenticators(?GLOBAL), + {ok, [#{provider := emqx_authn_mysql, state := State}]} = + emqx_authentication:list_authenticators(?GLOBAL), {ok, _} = emqx_authn_mysql:authenticate( - #{username => <<"plain">>, - password => <<"plain">> - }, - State), + #{ + username => <<"plain">>, + password => <<"plain">> + }, + State + ), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), % Authenticator should not be usable anymore ?assertMatch( - ignore, - emqx_authn_mysql:authenticate( - #{username => <<"plain">>, - password => <<"plain">> - }, - State)). + ignore, + emqx_authn_mysql:authenticate( + #{ + username => <<"plain">>, + password => <<"plain">> + }, + State + ) + ). t_update(_Config) -> CorrectConfig = raw_mysql_auth_config(), IncorrectConfig = CorrectConfig#{ - query => <<"SELECT password_hash, salt, is_superuser_str as is_superuser - FROM wrong_table where username = ${username} LIMIT 1">>}, + query => + << + "SELECT password_hash, salt, is_superuser_str as is_superuser\n" + " FROM wrong_table where username = ${username} LIMIT 1" + >> + }, {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, IncorrectConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, IncorrectConfig} + ), {error, not_authorized} = emqx_access_control:authenticate( - #{username => <<"plain">>, - password => <<"plain">>, - listener => 'tcp:default', - protocol => mqtt - }), + #{ + username => <<"plain">>, + password => <<"plain">>, + listener => 'tcp:default', + protocol => mqtt + } + ), % We update with config with correct query, provider should update and work properly {ok, _} = emqx:update_config( - ?PATH, - {update_authenticator, ?GLOBAL, <<"password_based:mysql">>, CorrectConfig}), + ?PATH, + {update_authenticator, ?GLOBAL, <<"password_based:mysql">>, CorrectConfig} + ), - {ok,_} = emqx_access_control:authenticate( - #{username => <<"plain">>, - password => <<"plain">>, - listener => 'tcp:default', - protocol => mqtt - }). + {ok, _} = emqx_access_control:authenticate( + #{ + username => <<"plain">>, + password => <<"plain">>, + listener => 'tcp:default', + protocol => mqtt + } + ). %%------------------------------------------------------------------------------ %% Helpers @@ -205,207 +233,248 @@ t_update(_Config) -> raw_mysql_auth_config() -> #{ - mechanism => <<"password_based">>, - password_hash_algorithm => #{name => <<"plain">>, - salt_position => <<"suffix">>}, - enable => <<"true">>, + mechanism => <<"password_based">>, + password_hash_algorithm => #{ + name => <<"plain">>, + salt_position => <<"suffix">> + }, + enable => <<"true">>, - backend => <<"mysql">>, - database => <<"mqtt">>, - username => <<"root">>, - password => <<"public">>, + backend => <<"mysql">>, + database => <<"mqtt">>, + username => <<"root">>, + password => <<"public">>, - query => <<"SELECT password_hash, salt, is_superuser_str as is_superuser - FROM users where username = ${username} LIMIT 1">>, - server => mysql_server() - }. + query => + << + "SELECT password_hash, salt, is_superuser_str as is_superuser\n" + " FROM users where username = ${username} LIMIT 1" + >>, + server => mysql_server() + }. user_seeds() -> - [#{data => #{ - username => "plain", - password_hash => "plainsalt", - salt => "salt", - is_superuser_str => "1" - }, - credentials => #{ - username => <<"plain">>, - password => <<"plain">>}, - config_params => #{}, - result => {ok,#{is_superuser => true}} - }, - - #{data => #{ - username => "md5", - password_hash => "9b4d0c43d206d48279e69b9ad7132e22", - salt => "salt", - is_superuser_str => "0" - }, - credentials => #{ - username => <<"md5">>, - password => <<"md5">> - }, - config_params => #{ - password_hash_algorithm => #{name => <<"md5">>, - salt_position => <<"suffix">>} - }, - result => {ok,#{is_superuser => false}} - }, - - #{data => #{ - username => "sha256", - password_hash => "ac63a624e7074776d677dd61a003b8c803eb11db004d0ec6ae032a5d7c9c5caf", - salt => "salt", - is_superuser_int => 1 + [ + #{ + data => #{ + username => "plain", + password_hash => "plainsalt", + salt => "salt", + is_superuser_str => "1" + }, + credentials => #{ + username => <<"plain">>, + password => <<"plain">> + }, + config_params => #{}, + result => {ok, #{is_superuser => true}} }, - credentials => #{ - clientid => <<"sha256">>, - password => <<"sha256">> - }, - config_params => #{ - query => <<"SELECT password_hash, salt, is_superuser_int as is_superuser - FROM users where username = ${clientid} LIMIT 1">>, - password_hash_algorithm => #{name => <<"sha256">>, - salt_position => <<"prefix">>} - }, - result => {ok,#{is_superuser => true}} - }, - #{data => #{ - username => <<"bcrypt">>, - password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", - salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", - is_superuser_int => 0 - }, - credentials => #{ - username => <<"bcrypt">>, - password => <<"bcrypt">> - }, - config_params => #{ - query => <<"SELECT password_hash, salt, is_superuser_int as is_superuser - FROM users where username = ${username} LIMIT 1">>, - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {ok,#{is_superuser => false}} - }, + #{ + data => #{ + username => "md5", + password_hash => "9b4d0c43d206d48279e69b9ad7132e22", + salt => "salt", + is_superuser_str => "0" + }, + credentials => #{ + username => <<"md5">>, + password => <<"md5">> + }, + config_params => #{ + password_hash_algorithm => #{ + name => <<"md5">>, + salt_position => <<"suffix">> + } + }, + result => {ok, #{is_superuser => false}} + }, - #{data => #{ - username => <<"bcrypt">>, - password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", - salt => "$2b$12$wtY3h20mUjjmeaClpqZVve" - }, - credentials => #{ - username => <<"bcrypt">>, - password => <<"bcrypt">> - }, - config_params => #{ - query => <<"SELECT password_hash, salt, is_superuser_int as is_superuser - FROM users where username = ${username} LIMIT 1">>, - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {ok,#{is_superuser => false}} - }, + #{ + data => #{ + username => "sha256", + password_hash => "ac63a624e7074776d677dd61a003b8c803eb11db004d0ec6ae032a5d7c9c5caf", + salt => "salt", + is_superuser_int => 1 + }, + credentials => #{ + clientid => <<"sha256">>, + password => <<"sha256">> + }, + config_params => #{ + query => + << + "SELECT password_hash, salt, is_superuser_int as is_superuser\n" + " FROM users where username = ${clientid} LIMIT 1" + >>, + password_hash_algorithm => #{ + name => <<"sha256">>, + salt_position => <<"prefix">> + } + }, + result => {ok, #{is_superuser => true}} + }, - #{data => #{ - username => <<"bcrypt0">>, - password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", - salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", - is_superuser_str => "0" - }, - credentials => #{ - username => <<"bcrypt0">>, - password => <<"bcrypt">> - }, - config_params => #{ - % clientid variable & username credentials - query => <<"SELECT password_hash, salt, is_superuser_int as is_superuser - FROM users where username = ${clientid} LIMIT 1">>, - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {error,not_authorized} - }, + #{ + data => #{ + username => <<"bcrypt">>, + password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", + salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", + is_superuser_int => 0 + }, + credentials => #{ + username => <<"bcrypt">>, + password => <<"bcrypt">> + }, + config_params => #{ + query => + << + "SELECT password_hash, salt, is_superuser_int as is_superuser\n" + " FROM users where username = ${username} LIMIT 1" + >>, + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {ok, #{is_superuser => false}} + }, - #{data => #{ - username => <<"bcrypt1">>, - password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", - salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", - is_superuser_str => "0" - }, - credentials => #{ - username => <<"bcrypt1">>, - password => <<"bcrypt">> - }, - config_params => #{ - % Bad keys in query - query => <<"SELECT 1 AS unknown_field - FROM users where username = ${username} LIMIT 1">>, - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {error,not_authorized} - }, + #{ + data => #{ + username => <<"bcrypt">>, + password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", + salt => "$2b$12$wtY3h20mUjjmeaClpqZVve" + }, + credentials => #{ + username => <<"bcrypt">>, + password => <<"bcrypt">> + }, + config_params => #{ + query => + << + "SELECT password_hash, salt, is_superuser_int as is_superuser\n" + " FROM users where username = ${username} LIMIT 1" + >>, + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {ok, #{is_superuser => false}} + }, - #{data => #{ - username => <<"bcrypt2">>, - password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", - salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", - is_superuser => "0" - }, - credentials => #{ - username => <<"bcrypt2">>, - % Wrong password - password => <<"wrongpass">> - }, - config_params => #{ - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {error,bad_username_or_password} - } + #{ + data => #{ + username => <<"bcrypt0">>, + password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", + salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", + is_superuser_str => "0" + }, + credentials => #{ + username => <<"bcrypt0">>, + password => <<"bcrypt">> + }, + config_params => #{ + % clientid variable & username credentials + query => + << + "SELECT password_hash, salt, is_superuser_int as is_superuser\n" + " FROM users where username = ${clientid} LIMIT 1" + >>, + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {error, not_authorized} + }, + + #{ + data => #{ + username => <<"bcrypt1">>, + password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", + salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", + is_superuser_str => "0" + }, + credentials => #{ + username => <<"bcrypt1">>, + password => <<"bcrypt">> + }, + config_params => #{ + % Bad keys in query + query => + << + "SELECT 1 AS unknown_field\n" + " FROM users where username = ${username} LIMIT 1" + >>, + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {error, not_authorized} + }, + + #{ + data => #{ + username => <<"bcrypt2">>, + password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", + salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", + is_superuser => "0" + }, + credentials => #{ + username => <<"bcrypt2">>, + % Wrong password + password => <<"wrongpass">> + }, + config_params => #{ + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {error, bad_username_or_password} + } ]. init_seeds() -> ok = drop_seeds(), - ok = q("CREATE TABLE users( - username VARCHAR(255), - password_hash VARCHAR(255), - salt VARCHAR(255), - is_superuser_str VARCHAR(255), - is_superuser_int TINYINT)"), + ok = q( + "CREATE TABLE users(\n" + " username VARCHAR(255),\n" + " password_hash VARCHAR(255),\n" + " salt VARCHAR(255),\n" + " is_superuser_str VARCHAR(255),\n" + " is_superuser_int TINYINT)" + ), Fields = [username, password_hash, salt, is_superuser_str, is_superuser_int], - InsertQuery = "INSERT INTO users(username, password_hash, salt, " - " is_superuser_str, is_superuser_int) VALUES(?, ?, ?, ?, ?)", + InsertQuery = + "INSERT INTO users(username, password_hash, salt, " + " is_superuser_str, is_superuser_int) VALUES(?, ?, ?, ?, ?)", lists:foreach( - fun(#{data := Values}) -> - Params = [maps:get(F, Values, null) || F <- Fields], - ok = q(InsertQuery, Params) - end, - user_seeds()). + fun(#{data := Values}) -> + Params = [maps:get(F, Values, null) || F <- Fields], + ok = q(InsertQuery, Params) + end, + user_seeds() + ). q(Sql) -> emqx_resource:query( - ?MYSQL_RESOURCE, - {sql, Sql}). + ?MYSQL_RESOURCE, + {sql, Sql} + ). q(Sql, Params) -> emqx_resource:query( - ?MYSQL_RESOURCE, - {sql, Sql, Params}). + ?MYSQL_RESOURCE, + {sql, Sql, Params} + ). drop_seeds() -> ok = q("DROP TABLE IF EXISTS users"). mysql_server() -> - iolist_to_binary(io_lib:format("~s",[?MYSQL_HOST])). + iolist_to_binary(io_lib:format("~s", [?MYSQL_HOST])). mysql_config() -> - #{auto_reconnect => true, - database => <<"mqtt">>, - username => <<"root">>, - password => <<"public">>, - pool_size => 8, - server => {?MYSQL_HOST, ?MYSQL_DEFAULT_PORT}, - ssl => #{enable => false} - }. + #{ + auto_reconnect => true, + database => <<"mqtt">>, + username => <<"root">>, + password => <<"public">>, + pool_size => 8, + server => {?MYSQL_HOST, ?MYSQL_DEFAULT_PORT}, + ssl => #{enable => false} + }. start_apps(Apps) -> lists:foreach(fun application:ensure_all_started/1, Apps). diff --git a/apps/emqx_authn/test/emqx_authn_mysql_tls_SUITE.erl b/apps/emqx_authn/test/emqx_authn_mysql_tls_SUITE.erl index e1404eb60..d7d655246 100644 --- a/apps/emqx_authn/test/emqx_authn_mysql_tls_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_mysql_tls_SUITE.erl @@ -39,8 +39,9 @@ init_per_testcase(_, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), emqx_authentication:initialize_authentication(?GLOBAL, []), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), Config. init_per_suite(Config) -> @@ -56,8 +57,9 @@ init_per_suite(Config) -> end_per_suite(_Config) -> emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), ok = stop_apps([emqx_resource, emqx_connector]), ok = emqx_common_test_helpers:stop_apps([emqx_authn]). @@ -70,38 +72,53 @@ t_create(_Config) -> %% -connect authn-server:3306 -starttls mysql \ %% -cert client.crt -key client.key -CAfile ca.crt ?assertMatch( - {ok, _}, - create_mysql_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.2">>], - <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>]})). + {ok, _}, + create_mysql_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>] + } + ) + ). t_create_invalid(_Config) -> - %% invalid server_name ?assertMatch( - {ok, _}, - create_mysql_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server-unknown-host">>, - <<"verify">> => <<"verify_peer">>})), + {ok, _}, + create_mysql_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server-unknown-host">>, + <<"verify">> => <<"verify_peer">> + } + ) + ), emqx_authn_test_lib:delete_config(?ResourceID), %% incompatible versions ?assertMatch( - {ok, _}, - create_mysql_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.1">>]})), + {ok, _}, + create_mysql_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.1">>] + } + ) + ), emqx_authn_test_lib:delete_config(?ResourceID), %% incompatible ciphers ?assertMatch( - {ok, _}, - create_mysql_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.2">>], - <<"ciphers">> => [<<"ECDHE-ECDSA-AES128-GCM-SHA256">>]})). + {ok, _}, + create_mysql_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-ECDSA-AES128-GCM-SHA256">>] + } + ) + ). %%------------------------------------------------------------------------------ %% Helpers @@ -113,27 +130,33 @@ create_mysql_auth_with_ssl_opts(SpecificSSLOpts) -> raw_mysql_auth_config(SpecificSSLOpts) -> SSLOpts = maps:merge( - emqx_authn_test_lib:client_ssl_cert_opts(), - #{enable => <<"true">>}), + emqx_authn_test_lib:client_ssl_cert_opts(), + #{enable => <<"true">>} + ), #{ - mechanism => <<"password_based">>, - password_hash_algorithm => #{name => <<"plain">>, - salt_position => <<"suffix">>}, - enable => <<"true">>, + mechanism => <<"password_based">>, + password_hash_algorithm => #{ + name => <<"plain">>, + salt_position => <<"suffix">> + }, + enable => <<"true">>, - backend => <<"mysql">>, - database => <<"mqtt">>, - username => <<"root">>, - password => <<"public">>, + backend => <<"mysql">>, + database => <<"mqtt">>, + username => <<"root">>, + password => <<"public">>, - query => <<"SELECT password_hash, salt, is_superuser_str as is_superuser - FROM users where username = ${username} LIMIT 1">>, - server => mysql_server(), - ssl => maps:merge(SSLOpts, SpecificSSLOpts) - }. + query => + << + "SELECT password_hash, salt, is_superuser_str as is_superuser\n" + " FROM users where username = ${username} LIMIT 1" + >>, + server => mysql_server(), + ssl => maps:merge(SSLOpts, SpecificSSLOpts) + }. mysql_server() -> - iolist_to_binary(io_lib:format("~s",[?MYSQL_HOST])). + iolist_to_binary(io_lib:format("~s", [?MYSQL_HOST])). start_apps(Apps) -> lists:foreach(fun application:ensure_all_started/1, Apps). diff --git a/apps/emqx_authn/test/emqx_authn_password_hashing_SUITE.erl b/apps/emqx_authn/test/emqx_authn_password_hashing_SUITE.erl index 71ff2ff38..0052fcfe1 100644 --- a/apps/emqx_authn/test/emqx_authn_password_hashing_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_password_hashing_SUITE.erl @@ -38,118 +38,150 @@ end_per_suite(_Config) -> ok. t_gen_salt(_Config) -> - Algorithms = [#{name => Type, salt_position => suffix} || Type <- ?SIMPLE_HASHES] - ++ [#{name => bcrypt, salt_rounds => 10}], + Algorithms = + [#{name => Type, salt_position => suffix} || Type <- ?SIMPLE_HASHES] ++ + [#{name => bcrypt, salt_rounds => 10}], lists:foreach( - fun(Algorithm) -> - Salt = emqx_authn_password_hashing:gen_salt(Algorithm), - ct:pal("gen_salt(~p): ~p", [Algorithm, Salt]), - ?assert(is_binary(Salt)) - end, - Algorithms). + fun(Algorithm) -> + Salt = emqx_authn_password_hashing:gen_salt(Algorithm), + ct:pal("gen_salt(~p): ~p", [Algorithm, Salt]), + ?assert(is_binary(Salt)) + end, + Algorithms + ). t_init(_Config) -> - Algorithms = [#{name => Type, salt_position => suffix} || Type <- ?SIMPLE_HASHES] - ++ [#{name => bcrypt, salt_rounds => 10}], + Algorithms = + [#{name => Type, salt_position => suffix} || Type <- ?SIMPLE_HASHES] ++ + [#{name => bcrypt, salt_rounds => 10}], lists:foreach( - fun(Algorithm) -> - ok = emqx_authn_password_hashing:init(Algorithm) - end, - Algorithms). + fun(Algorithm) -> + ok = emqx_authn_password_hashing:init(Algorithm) + end, + Algorithms + ). t_check_password(_Config) -> lists:foreach( - fun test_check_password/1, - hash_examples()). + fun test_check_password/1, + hash_examples() + ). -test_check_password(#{ - password_hash := Hash, - salt := Salt, - password := Password, - password_hash_algorithm := Algorithm - } = Sample) -> +test_check_password( + #{ + password_hash := Hash, + salt := Salt, + password := Password, + password_hash_algorithm := Algorithm + } = Sample +) -> ct:pal("t_check_password sample: ~p", [Sample]), true = emqx_authn_password_hashing:check_password(Algorithm, Salt, Hash, Password), false = emqx_authn_password_hashing:check_password(Algorithm, Salt, Hash, <<"wrongpass">>). t_hash(_Config) -> lists:foreach( - fun test_hash/1, - hash_examples()). + fun test_hash/1, + hash_examples() + ). -test_hash(#{password := Password, - password_hash_algorithm := Algorithm - } = Sample) -> +test_hash( + #{ + password := Password, + password_hash_algorithm := Algorithm + } = Sample +) -> ct:pal("t_hash sample: ~p", [Sample]), {Hash, Salt} = emqx_authn_password_hashing:hash(Algorithm, Password), true = emqx_authn_password_hashing:check_password(Algorithm, Salt, Hash, Password). hash_examples() -> - [#{ - password_hash => <<"plainsalt">>, - salt => <<"salt">>, - password => <<"plain">>, - password_hash_algorithm => #{name => plain, - salt_position => suffix} - }, - #{ - password_hash => <<"9b4d0c43d206d48279e69b9ad7132e22">>, - salt => <<"salt">>, - password => <<"md5">>, - password_hash_algorithm => #{name => md5, - salt_position => suffix} - }, - #{ - password_hash => <<"c665d4c0a9e5498806b7d9fd0b417d272853660e">>, - salt => <<"salt">>, - password => <<"sha">>, - password_hash_algorithm => #{name => sha, - salt_position => prefix} - }, - #{ - password_hash => <<"ac63a624e7074776d677dd61a003b8c803eb11db004d0ec6ae032a5d7c9c5caf">>, - salt => <<"salt">>, - password => <<"sha256">>, - password_hash_algorithm => #{name => sha256, - salt_position => prefix} - }, - #{ - password_hash => <<"a1509ab67bfacbad020927b5ac9d91e9100a82e33a0ebb01459367ce921c0aa8" - "157aa5652f94bc84fa3babc08283e44887d61c48bcf8ad7bcb3259ee7d0eafcd">>, - salt => <<"salt">>, - password => <<"sha512">>, - password_hash_algorithm => #{name => sha512, - salt_position => prefix} - }, - #{ - password_hash => <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, - salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, - password => <<"bcrypt">>, + [ + #{ + password_hash => <<"plainsalt">>, + salt => <<"salt">>, + password => <<"plain">>, + password_hash_algorithm => #{ + name => plain, + salt_position => suffix + } + }, + #{ + password_hash => <<"9b4d0c43d206d48279e69b9ad7132e22">>, + salt => <<"salt">>, + password => <<"md5">>, + password_hash_algorithm => #{ + name => md5, + salt_position => suffix + } + }, + #{ + password_hash => <<"c665d4c0a9e5498806b7d9fd0b417d272853660e">>, + salt => <<"salt">>, + password => <<"sha">>, + password_hash_algorithm => #{ + name => sha, + salt_position => prefix + } + }, + #{ + password_hash => <<"ac63a624e7074776d677dd61a003b8c803eb11db004d0ec6ae032a5d7c9c5caf">>, + salt => <<"salt">>, + password => <<"sha256">>, + password_hash_algorithm => #{ + name => sha256, + salt_position => prefix + } + }, + #{ + password_hash => << + "a1509ab67bfacbad020927b5ac9d91e9100a82e33a0ebb01459367ce921c0aa8" + "157aa5652f94bc84fa3babc08283e44887d61c48bcf8ad7bcb3259ee7d0eafcd" + >>, + salt => <<"salt">>, + password => <<"sha512">>, + password_hash_algorithm => #{ + name => sha512, + salt_position => prefix + } + }, + #{ + password_hash => <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, + salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, + password => <<"bcrypt">>, - password_hash_algorithm => #{name => bcrypt, - salt_rounds => 10} - }, + password_hash_algorithm => #{ + name => bcrypt, + salt_rounds => 10 + } + }, - #{ - password_hash => <<"01dbee7f4a9e243e988b62c73cda935d" - "a05378b93244ec8f48a99e61ad799d86">>, - salt => <<"ATHENA.MIT.EDUraeburn">>, - password => <<"password">>, + #{ + password_hash => << + "01dbee7f4a9e243e988b62c73cda935d" + "a05378b93244ec8f48a99e61ad799d86" + >>, + salt => <<"ATHENA.MIT.EDUraeburn">>, + password => <<"password">>, - password_hash_algorithm => #{name => pbkdf2, - iterations => 2, - dk_length => 32, - mac_fun => sha} - }, - #{ - password_hash => <<"01dbee7f4a9e243e988b62c73cda935da05378b9">>, - salt => <<"ATHENA.MIT.EDUraeburn">>, - password => <<"password">>, + password_hash_algorithm => #{ + name => pbkdf2, + iterations => 2, + dk_length => 32, + mac_fun => sha + } + }, + #{ + password_hash => <<"01dbee7f4a9e243e988b62c73cda935da05378b9">>, + salt => <<"ATHENA.MIT.EDUraeburn">>, + password => <<"password">>, - password_hash_algorithm => #{name => pbkdf2, - iterations => 2, - mac_fun => sha} - } + password_hash_algorithm => #{ + name => pbkdf2, + iterations => 2, + mac_fun => sha + } + } ]. diff --git a/apps/emqx_authn/test/emqx_authn_pgsql_SUITE.erl b/apps/emqx_authn/test/emqx_authn_pgsql_SUITE.erl index 4b5606735..269825dce 100644 --- a/apps/emqx_authn/test/emqx_authn_pgsql_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_pgsql_SUITE.erl @@ -41,8 +41,9 @@ init_per_testcase(_, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), emqx_authentication:initialize_authentication(?GLOBAL, []), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), Config. init_per_group(require_seeds, Config) -> @@ -60,11 +61,12 @@ init_per_suite(Config) -> ok = emqx_common_test_helpers:start_apps([emqx_authn]), ok = start_apps([emqx_resource, emqx_connector]), {ok, _} = emqx_resource:create_local( - ?PGSQL_RESOURCE, - ?RESOURCE_GROUP, - emqx_connector_pgsql, - pgsql_config(), - #{}), + ?PGSQL_RESOURCE, + ?RESOURCE_GROUP, + emqx_connector_pgsql, + pgsql_config(), + #{} + ), Config; false -> {skip, no_pgsql} @@ -72,8 +74,9 @@ init_per_suite(Config) -> end_per_suite(_Config) -> emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), ok = emqx_resource:remove_local(?PGSQL_RESOURCE), ok = stop_apps([emqx_resource, emqx_connector]), ok = emqx_common_test_helpers:stop_apps([emqx_authn]). @@ -86,8 +89,9 @@ t_create(_Config) -> AuthConfig = raw_pgsql_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), {ok, [#{provider := emqx_authn_pgsql}]} = emqx_authentication:list_authenticators(?GLOBAL), emqx_authn_test_lib:delete_config(?ResourceID). @@ -97,131 +101,156 @@ t_create_invalid(_Config) -> InvalidConfigs = [ - maps:without([server], AuthConfig), - AuthConfig#{server => <<"unknownhost:3333">>}, - AuthConfig#{password => <<"wrongpass">>}, - AuthConfig#{database => <<"wrongdatabase">>} + maps:without([server], AuthConfig), + AuthConfig#{server => <<"unknownhost:3333">>}, + AuthConfig#{password => <<"wrongpass">>}, + AuthConfig#{database => <<"wrongdatabase">>} ], lists:foreach( - fun(Config) -> - {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, Config}), - emqx_authn_test_lib:delete_config(?ResourceID), - {ok, []} = emqx_authentication:list_authenticators(?GLOBAL) - end, - InvalidConfigs). + fun(Config) -> + {ok, _} = emqx:update_config( + ?PATH, + {create_authenticator, ?GLOBAL, Config} + ), + emqx_authn_test_lib:delete_config(?ResourceID), + {ok, []} = emqx_authentication:list_authenticators(?GLOBAL) + end, + InvalidConfigs + ). t_authenticate(_Config) -> ok = lists:foreach( - fun(Sample) -> - ct:pal("test_user_auth sample: ~p", [Sample]), - test_user_auth(Sample) - end, - user_seeds()). + fun(Sample) -> + ct:pal("test_user_auth sample: ~p", [Sample]), + test_user_auth(Sample) + end, + user_seeds() + ). -test_user_auth(#{credentials := Credentials0, - config_params := SpecificConfigParams, - result := Result}) -> +test_user_auth(#{ + credentials := Credentials0, + config_params := SpecificConfigParams, + result := Result +}) -> AuthConfig = maps:merge(raw_pgsql_auth_config(), SpecificConfigParams), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), Credentials = Credentials0#{ - listener => 'tcp:default', - protocol => mqtt - }, + listener => 'tcp:default', + protocol => mqtt + }, ?assertEqual(Result, emqx_access_control:authenticate(Credentials)), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL). + [authentication], + ?GLOBAL + ). t_destroy(_Config) -> AuthConfig = raw_pgsql_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), - {ok, [#{provider := emqx_authn_pgsql, state := State}]} - = emqx_authentication:list_authenticators(?GLOBAL), + {ok, [#{provider := emqx_authn_pgsql, state := State}]} = + emqx_authentication:list_authenticators(?GLOBAL), {ok, _} = emqx_authn_pgsql:authenticate( - #{username => <<"plain">>, - password => <<"plain">> - }, - State), + #{ + username => <<"plain">>, + password => <<"plain">> + }, + State + ), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), % Authenticator should not be usable anymore ?assertMatch( - ignore, - emqx_authn_pgsql:authenticate( - #{username => <<"plain">>, - password => <<"plain">> - }, - State)). + ignore, + emqx_authn_pgsql:authenticate( + #{ + username => <<"plain">>, + password => <<"plain">> + }, + State + ) + ). t_update(_Config) -> CorrectConfig = raw_pgsql_auth_config(), IncorrectConfig = CorrectConfig#{ - query => <<"SELECT password_hash, salt, is_superuser_str as is_superuser - FROM users where username = ${username} LIMIT 0">>}, + query => + << + "SELECT password_hash, salt, is_superuser_str as is_superuser\n" + " FROM users where username = ${username} LIMIT 0" + >> + }, {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, IncorrectConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, IncorrectConfig} + ), {error, not_authorized} = emqx_access_control:authenticate( - #{username => <<"plain">>, - password => <<"plain">>, - listener => 'tcp:default', - protocol => mqtt - }), + #{ + username => <<"plain">>, + password => <<"plain">>, + listener => 'tcp:default', + protocol => mqtt + } + ), % We update with config with correct query, provider should update and work properly {ok, _} = emqx:update_config( - ?PATH, - {update_authenticator, ?GLOBAL, <<"password_based:postgresql">>, CorrectConfig}), + ?PATH, + {update_authenticator, ?GLOBAL, <<"password_based:postgresql">>, CorrectConfig} + ), - {ok,_} = emqx_access_control:authenticate( - #{username => <<"plain">>, - password => <<"plain">>, - listener => 'tcp:default', - protocol => mqtt - }). + {ok, _} = emqx_access_control:authenticate( + #{ + username => <<"plain">>, + password => <<"plain">>, + listener => 'tcp:default', + protocol => mqtt + } + ). t_is_superuser(_Config) -> Config = raw_pgsql_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, Config}), + ?PATH, + {create_authenticator, ?GLOBAL, Config} + ), Checks = [ - {is_superuser_str, "0", false}, - {is_superuser_str, "", false}, - {is_superuser_str, null, false}, - {is_superuser_str, "1", true}, - {is_superuser_str, "val", true}, + {is_superuser_str, "0", false}, + {is_superuser_str, "", false}, + {is_superuser_str, null, false}, + {is_superuser_str, "1", true}, + {is_superuser_str, "val", true}, - {is_superuser_int, 0, false}, - {is_superuser_int, null, false}, - {is_superuser_int, 1, true}, - {is_superuser_int, 123, true}, + {is_superuser_int, 0, false}, + {is_superuser_int, null, false}, + {is_superuser_int, 1, true}, + {is_superuser_int, 123, true}, - {is_superuser_bool, false, false}, - {is_superuser_bool, null, false}, - {is_superuser_bool, true, true} - ], + {is_superuser_bool, false, false}, + {is_superuser_bool, null, false}, + {is_superuser_bool, true, true} + ], lists:foreach(fun test_is_superuser/1, Checks). @@ -229,32 +258,36 @@ test_is_superuser({Field, Value, ExpectedValue}) -> {ok, _} = q("DELETE FROM users"), UserData = #{ - username => "user", - password_hash => "plainsalt", - salt => "salt", - Field => Value - }, + username => "user", + password_hash => "plainsalt", + salt => "salt", + Field => Value + }, ok = create_user(UserData), - Query = "SELECT password_hash, salt, " ++ atom_to_list(Field) ++ " as is_superuser " - "FROM users where username = ${username} LIMIT 1", + Query = + "SELECT password_hash, salt, " ++ atom_to_list(Field) ++ + " as is_superuser " + "FROM users where username = ${username} LIMIT 1", Config = maps:put(query, Query, raw_pgsql_auth_config()), {ok, _} = emqx:update_config( - ?PATH, - {update_authenticator, ?GLOBAL, <<"password_based:postgresql">>, Config}), + ?PATH, + {update_authenticator, ?GLOBAL, <<"password_based:postgresql">>, Config} + ), Credentials = #{ - listener => 'tcp:default', - protocol => mqtt, - username => <<"user">>, - password => <<"plain">> - }, + listener => 'tcp:default', + protocol => mqtt, + username => <<"user">>, + password => <<"plain">> + }, ?assertEqual( - {ok, #{is_superuser => ExpectedValue}}, - emqx_access_control:authenticate(Credentials)). + {ok, #{is_superuser => ExpectedValue}}, + emqx_access_control:authenticate(Credentials) + ). %%------------------------------------------------------------------------------ %% Helpers @@ -262,167 +295,201 @@ test_is_superuser({Field, Value, ExpectedValue}) -> raw_pgsql_auth_config() -> #{ - mechanism => <<"password_based">>, - password_hash_algorithm => #{name => <<"plain">>, - salt_position => <<"suffix">>}, - enable => <<"true">>, + mechanism => <<"password_based">>, + password_hash_algorithm => #{ + name => <<"plain">>, + salt_position => <<"suffix">> + }, + enable => <<"true">>, - backend => <<"postgresql">>, - database => <<"mqtt">>, - username => <<"root">>, - password => <<"public">>, + backend => <<"postgresql">>, + database => <<"mqtt">>, + username => <<"root">>, + password => <<"public">>, - query => <<"SELECT password_hash, salt, is_superuser_str as is_superuser - FROM users where username = ${username} LIMIT 1">>, - server => pgsql_server() - }. + query => + << + "SELECT password_hash, salt, is_superuser_str as is_superuser\n" + " FROM users where username = ${username} LIMIT 1" + >>, + server => pgsql_server() + }. user_seeds() -> - [#{data => #{ - username => "plain", - password_hash => "plainsalt", - salt => "salt", - is_superuser_str => "1" - }, - credentials => #{ - username => <<"plain">>, - password => <<"plain">>}, - config_params => #{}, - result => {ok,#{is_superuser => true}} - }, - - #{data => #{ - username => "md5", - password_hash => "9b4d0c43d206d48279e69b9ad7132e22", - salt => "salt", - is_superuser_str => "0" - }, - credentials => #{ - username => <<"md5">>, - password => <<"md5">> - }, - config_params => #{ - password_hash_algorithm => #{name => <<"md5">>, - salt_position => <<"suffix">>} - }, - result => {ok,#{is_superuser => false}} - }, - - #{data => #{ - username => "sha256", - password_hash => "ac63a624e7074776d677dd61a003b8c803eb11db004d0ec6ae032a5d7c9c5caf", - salt => "salt", - is_superuser_int => 1 + [ + #{ + data => #{ + username => "plain", + password_hash => "plainsalt", + salt => "salt", + is_superuser_str => "1" + }, + credentials => #{ + username => <<"plain">>, + password => <<"plain">> + }, + config_params => #{}, + result => {ok, #{is_superuser => true}} }, - credentials => #{ - clientid => <<"sha256">>, - password => <<"sha256">> - }, - config_params => #{ - query => <<"SELECT password_hash, salt, is_superuser_int as is_superuser - FROM users where username = ${clientid} LIMIT 1">>, - password_hash_algorithm => #{name => <<"sha256">>, - salt_position => <<"prefix">>} - }, - result => {ok,#{is_superuser => true}} - }, - #{data => #{ - username => <<"bcrypt">>, - password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", - salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", - is_superuser_int => 0 - }, - credentials => #{ - username => <<"bcrypt">>, - password => <<"bcrypt">> - }, - config_params => #{ - query => <<"SELECT password_hash, salt, is_superuser_int as is_superuser - FROM users where username = ${username} LIMIT 1">>, - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {ok,#{is_superuser => false}} - }, + #{ + data => #{ + username => "md5", + password_hash => "9b4d0c43d206d48279e69b9ad7132e22", + salt => "salt", + is_superuser_str => "0" + }, + credentials => #{ + username => <<"md5">>, + password => <<"md5">> + }, + config_params => #{ + password_hash_algorithm => #{ + name => <<"md5">>, + salt_position => <<"suffix">> + } + }, + result => {ok, #{is_superuser => false}} + }, - #{data => #{ - username => <<"bcrypt0">>, - password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", - salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", - is_superuser_str => "0" - }, - credentials => #{ - username => <<"bcrypt0">>, - password => <<"bcrypt">> - }, - config_params => #{ - % clientid variable & username credentials - query => <<"SELECT password_hash, salt, is_superuser_int as is_superuser - FROM users where username = ${clientid} LIMIT 1">>, - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {error,not_authorized} - }, + #{ + data => #{ + username => "sha256", + password_hash => "ac63a624e7074776d677dd61a003b8c803eb11db004d0ec6ae032a5d7c9c5caf", + salt => "salt", + is_superuser_int => 1 + }, + credentials => #{ + clientid => <<"sha256">>, + password => <<"sha256">> + }, + config_params => #{ + query => + << + "SELECT password_hash, salt, is_superuser_int as is_superuser\n" + " FROM users where username = ${clientid} LIMIT 1" + >>, + password_hash_algorithm => #{ + name => <<"sha256">>, + salt_position => <<"prefix">> + } + }, + result => {ok, #{is_superuser => true}} + }, - #{data => #{ - username => <<"bcrypt1">>, - password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", - salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", - is_superuser_str => "0" - }, - credentials => #{ - username => <<"bcrypt1">>, - password => <<"bcrypt">> - }, - config_params => #{ - % Bad keys in query - query => <<"SELECT 1 AS unknown_field - FROM users where username = ${username} LIMIT 1">>, - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {error,not_authorized} - }, + #{ + data => #{ + username => <<"bcrypt">>, + password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", + salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", + is_superuser_int => 0 + }, + credentials => #{ + username => <<"bcrypt">>, + password => <<"bcrypt">> + }, + config_params => #{ + query => + << + "SELECT password_hash, salt, is_superuser_int as is_superuser\n" + " FROM users where username = ${username} LIMIT 1" + >>, + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {ok, #{is_superuser => false}} + }, - #{data => #{ - username => <<"bcrypt2">>, - password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", - salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", - is_superuser => "0" - }, - credentials => #{ - username => <<"bcrypt2">>, - % Wrong password - password => <<"wrongpass">> - }, - config_params => #{ - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {error,bad_username_or_password} - } + #{ + data => #{ + username => <<"bcrypt0">>, + password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", + salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", + is_superuser_str => "0" + }, + credentials => #{ + username => <<"bcrypt0">>, + password => <<"bcrypt">> + }, + config_params => #{ + % clientid variable & username credentials + query => + << + "SELECT password_hash, salt, is_superuser_int as is_superuser\n" + " FROM users where username = ${clientid} LIMIT 1" + >>, + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {error, not_authorized} + }, + + #{ + data => #{ + username => <<"bcrypt1">>, + password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", + salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", + is_superuser_str => "0" + }, + credentials => #{ + username => <<"bcrypt1">>, + password => <<"bcrypt">> + }, + config_params => #{ + % Bad keys in query + query => + << + "SELECT 1 AS unknown_field\n" + " FROM users where username = ${username} LIMIT 1" + >>, + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {error, not_authorized} + }, + + #{ + data => #{ + username => <<"bcrypt2">>, + password_hash => "$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u", + salt => "$2b$12$wtY3h20mUjjmeaClpqZVve", + is_superuser => "0" + }, + credentials => #{ + username => <<"bcrypt2">>, + % Wrong password + password => <<"wrongpass">> + }, + config_params => #{ + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {error, bad_username_or_password} + } ]. init_seeds() -> ok = drop_seeds(), - {ok, _, _} = q("CREATE TABLE users( - username varchar(255), - password_hash varchar(255), - salt varchar(255), - is_superuser_str varchar(255), - is_superuser_int smallint, - is_superuser_bool boolean)"), + {ok, _, _} = q( + "CREATE TABLE users(\n" + " username varchar(255),\n" + " password_hash varchar(255),\n" + " salt varchar(255),\n" + " is_superuser_str varchar(255),\n" + " is_superuser_int smallint,\n" + " is_superuser_bool boolean)" + ), lists:foreach( - fun(#{data := Values}) -> - ok = create_user(Values) - end, - user_seeds()). + fun(#{data := Values}) -> + ok = create_user(Values) + end, + user_seeds() + ). create_user(Values) -> Fields = [username, password_hash, salt, is_superuser_str, is_superuser_int, is_superuser_bool], - InsertQuery = "INSERT INTO users(username, password_hash, salt," - "is_superuser_str, is_superuser_int, is_superuser_bool) " - "VALUES($1, $2, $3, $4, $5, $6)", + InsertQuery = + "INSERT INTO users(username, password_hash, salt," + "is_superuser_str, is_superuser_int, is_superuser_bool) " + "VALUES($1, $2, $3, $4, $5, $6)", Params = [maps:get(F, Values, null) || F <- Fields], {ok, 1} = q(InsertQuery, Params), @@ -430,30 +497,33 @@ create_user(Values) -> q(Sql) -> emqx_resource:query( - ?PGSQL_RESOURCE, - {query, Sql}). + ?PGSQL_RESOURCE, + {query, Sql} + ). q(Sql, Params) -> emqx_resource:query( - ?PGSQL_RESOURCE, - {query, Sql, Params}). + ?PGSQL_RESOURCE, + {query, Sql, Params} + ). drop_seeds() -> {ok, _, _} = q("DROP TABLE IF EXISTS users"), ok. pgsql_server() -> - iolist_to_binary(io_lib:format("~s",[?PGSQL_HOST])). + iolist_to_binary(io_lib:format("~s", [?PGSQL_HOST])). pgsql_config() -> - #{auto_reconnect => true, - database => <<"mqtt">>, - username => <<"root">>, - password => <<"public">>, - pool_size => 8, - server => {?PGSQL_HOST, ?PGSQL_DEFAULT_PORT}, - ssl => #{enable => false} - }. + #{ + auto_reconnect => true, + database => <<"mqtt">>, + username => <<"root">>, + password => <<"public">>, + pool_size => 8, + server => {?PGSQL_HOST, ?PGSQL_DEFAULT_PORT}, + ssl => #{enable => false} + }. start_apps(Apps) -> lists:foreach(fun application:ensure_all_started/1, Apps). diff --git a/apps/emqx_authn/test/emqx_authn_pgsql_tls_SUITE.erl b/apps/emqx_authn/test/emqx_authn_pgsql_tls_SUITE.erl index 0039b7239..300cd748e 100644 --- a/apps/emqx_authn/test/emqx_authn_pgsql_tls_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_pgsql_tls_SUITE.erl @@ -39,8 +39,9 @@ init_per_testcase(_, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), emqx_authentication:initialize_authentication(?GLOBAL, []), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), Config. init_per_suite(Config) -> @@ -56,8 +57,9 @@ init_per_suite(Config) -> end_per_suite(_Config) -> emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), ok = stop_apps([emqx_resource, emqx_connector]), ok = emqx_common_test_helpers:stop_apps([emqx_authn]). @@ -70,38 +72,53 @@ t_create(_Config) -> %% -starttls postgres -connect authn-server:5432 \ %% -cert client.crt -key client.key -CAfile ca.crt ?assertMatch( - {ok, _}, - create_pgsql_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.2">>], - <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>]})). + {ok, _}, + create_pgsql_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>] + } + ) + ). t_create_invalid(_Config) -> - %% invalid server_name ?assertMatch( - {ok, _}, - create_pgsql_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server-unknown-host">>, - <<"verify">> => <<"verify_peer">>})), + {ok, _}, + create_pgsql_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server-unknown-host">>, + <<"verify">> => <<"verify_peer">> + } + ) + ), emqx_authn_test_lib:delete_config(?ResourceID), %% incompatible versions ?assertMatch( - {ok, _}, - create_pgsql_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.1">>]})), + {ok, _}, + create_pgsql_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.1">>] + } + ) + ), emqx_authn_test_lib:delete_config(?ResourceID), %% incompatible ciphers ?assertMatch( - {ok, _}, - create_pgsql_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.2">>], - <<"ciphers">> => [<<"ECDHE-ECDSA-AES128-GCM-SHA256">>]})). + {ok, _}, + create_pgsql_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-ECDSA-AES128-GCM-SHA256">>] + } + ) + ). %%------------------------------------------------------------------------------ %% Helpers @@ -113,26 +130,29 @@ create_pgsql_auth_with_ssl_opts(SpecificSSLOpts) -> raw_pgsql_auth_config(SpecificSSLOpts) -> SSLOpts = maps:merge( - emqx_authn_test_lib:client_ssl_cert_opts(), - #{enable => <<"true">>}), + emqx_authn_test_lib:client_ssl_cert_opts(), + #{enable => <<"true">>} + ), #{ - mechanism => <<"password_based">>, - password_hash_algorithm => #{name => <<"plain">>, - salt_position => <<"suffix">>}, - enable => <<"true">>, + mechanism => <<"password_based">>, + password_hash_algorithm => #{ + name => <<"plain">>, + salt_position => <<"suffix">> + }, + enable => <<"true">>, - backend => <<"postgresql">>, - database => <<"mqtt">>, - username => <<"root">>, - password => <<"public">>, + backend => <<"postgresql">>, + database => <<"mqtt">>, + username => <<"root">>, + password => <<"public">>, - query => <<"SELECT 1">>, - server => pgsql_server(), - ssl => maps:merge(SSLOpts, SpecificSSLOpts) - }. + query => <<"SELECT 1">>, + server => pgsql_server(), + ssl => maps:merge(SSLOpts, SpecificSSLOpts) + }. pgsql_server() -> - iolist_to_binary(io_lib:format("~s",[?PGSQL_HOST])). + iolist_to_binary(io_lib:format("~s", [?PGSQL_HOST])). start_apps(Apps) -> lists:foreach(fun application:ensure_all_started/1, Apps). diff --git a/apps/emqx_authn/test/emqx_authn_redis_SUITE.erl b/apps/emqx_authn/test/emqx_authn_redis_SUITE.erl index 2cf476be7..ade356a3c 100644 --- a/apps/emqx_authn/test/emqx_authn_redis_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_redis_SUITE.erl @@ -40,8 +40,9 @@ init_per_testcase(_, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), emqx_authentication:initialize_authentication(?GLOBAL, []), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), Config. init_per_group(require_seeds, Config) -> @@ -59,11 +60,12 @@ init_per_suite(Config) -> ok = emqx_common_test_helpers:start_apps([emqx_authn]), ok = start_apps([emqx_resource, emqx_connector]), {ok, _} = emqx_resource:create_local( - ?REDIS_RESOURCE, - ?RESOURCE_GROUP, - emqx_connector_redis, - redis_config(), - #{}), + ?REDIS_RESOURCE, + ?RESOURCE_GROUP, + emqx_connector_redis, + redis_config(), + #{} + ), Config; false -> {skip, no_redis} @@ -71,8 +73,9 @@ init_per_suite(Config) -> end_per_suite(_Config) -> emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), ok = emqx_resource:remove_local(?REDIS_RESOURCE), ok = stop_apps([emqx_resource, emqx_connector]), ok = emqx_common_test_helpers:stop_apps([emqx_authn]). @@ -86,8 +89,9 @@ t_create(_Config) -> AuthConfig = raw_redis_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), {ok, [#{provider := emqx_authn_redis}]} = emqx_authentication:list_authenticators(?GLOBAL). @@ -95,126 +99,152 @@ t_create_invalid(_Config) -> AuthConfig = raw_redis_auth_config(), InvalidConfigs = [ - AuthConfig#{ - cmd => <<"MGET password_hash:${username} salt:${username}">>}, - AuthConfig#{ - cmd => <<"HMGET mqtt_user:${username} password_hash invalid_field">>}, - AuthConfig#{ - cmd => <<"HMGET mqtt_user:${username} salt is_superuser">>} + AuthConfig#{ + cmd => <<"MGET password_hash:${username} salt:${username}">> + }, + AuthConfig#{ + cmd => <<"HMGET mqtt_user:${username} password_hash invalid_field">> + }, + AuthConfig#{ + cmd => <<"HMGET mqtt_user:${username} salt is_superuser">> + } ], lists:foreach( - fun(Config) -> - {error, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, Config}), + fun(Config) -> + {error, _} = emqx:update_config( + ?PATH, + {create_authenticator, ?GLOBAL, Config} + ), - {ok, []} = emqx_authentication:list_authenticators(?GLOBAL) - end, - InvalidConfigs), + {ok, []} = emqx_authentication:list_authenticators(?GLOBAL) + end, + InvalidConfigs + ), InvalidConfigs1 = [ - maps:without([server], AuthConfig), - AuthConfig#{server => <<"unknownhost:3333">>}, - AuthConfig#{password => <<"wrongpass">>}, - AuthConfig#{database => <<"5678">>} + maps:without([server], AuthConfig), + AuthConfig#{server => <<"unknownhost:3333">>}, + AuthConfig#{password => <<"wrongpass">>}, + AuthConfig#{database => <<"5678">>} ], lists:foreach( - fun(Config) -> - {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, Config}), - emqx_authn_test_lib:delete_config(?ResourceID), - {ok, []} = emqx_authentication:list_authenticators(?GLOBAL) - end, - InvalidConfigs1). + fun(Config) -> + {ok, _} = emqx:update_config( + ?PATH, + {create_authenticator, ?GLOBAL, Config} + ), + emqx_authn_test_lib:delete_config(?ResourceID), + {ok, []} = emqx_authentication:list_authenticators(?GLOBAL) + end, + InvalidConfigs1 + ). t_authenticate(_Config) -> ok = lists:foreach( - fun(Sample) -> - ct:pal("test_user_auth sample: ~p", [Sample]), - test_user_auth(Sample) - end, - user_seeds()). + fun(Sample) -> + ct:pal("test_user_auth sample: ~p", [Sample]), + test_user_auth(Sample) + end, + user_seeds() + ). -test_user_auth(#{credentials := Credentials0, - config_params := SpecificConfigParams, - result := Result}) -> +test_user_auth(#{ + credentials := Credentials0, + config_params := SpecificConfigParams, + result := Result +}) -> AuthConfig = maps:merge(raw_redis_auth_config(), SpecificConfigParams), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), Credentials = Credentials0#{ - listener => 'tcp:default', - protocol => mqtt - }, + listener => 'tcp:default', + protocol => mqtt + }, ?assertEqual(Result, emqx_access_control:authenticate(Credentials)), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL). + [authentication], + ?GLOBAL + ). t_destroy(_Config) -> AuthConfig = raw_redis_auth_config(), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, AuthConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, AuthConfig} + ), - {ok, [#{provider := emqx_authn_redis, state := State}]} - = emqx_authentication:list_authenticators(?GLOBAL), + {ok, [#{provider := emqx_authn_redis, state := State}]} = + emqx_authentication:list_authenticators(?GLOBAL), {ok, _} = emqx_authn_redis:authenticate( - #{username => <<"plain">>, - password => <<"plain">> - }, - State), + #{ + username => <<"plain">>, + password => <<"plain">> + }, + State + ), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), % Authenticator should not be usable anymore ?assertMatch( - ignore, - emqx_authn_redis:authenticate( - #{username => <<"plain">>, - password => <<"plain">> - }, - State)). + ignore, + emqx_authn_redis:authenticate( + #{ + username => <<"plain">>, + password => <<"plain">> + }, + State + ) + ). t_update(_Config) -> CorrectConfig = raw_redis_auth_config(), IncorrectConfig = CorrectConfig#{ - cmd => <<"HMGET invalid_key:${username} password_hash salt is_superuser">>}, + cmd => <<"HMGET invalid_key:${username} password_hash salt is_superuser">> + }, {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, IncorrectConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, IncorrectConfig} + ), {error, not_authorized} = emqx_access_control:authenticate( - #{username => <<"plain">>, - password => <<"plain">>, - listener => 'tcp:default', - protocol => mqtt - }), + #{ + username => <<"plain">>, + password => <<"plain">>, + listener => 'tcp:default', + protocol => mqtt + } + ), % We update with config with correct query, provider should update and work properly {ok, _} = emqx:update_config( - ?PATH, - {update_authenticator, ?GLOBAL, <<"password_based:redis">>, CorrectConfig}), + ?PATH, + {update_authenticator, ?GLOBAL, <<"password_based:redis">>, CorrectConfig} + ), - {ok,_} = emqx_access_control:authenticate( - #{username => <<"plain">>, - password => <<"plain">>, - listener => 'tcp:default', - protocol => mqtt - }). + {ok, _} = emqx_access_control:authenticate( + #{ + username => <<"plain">>, + password => <<"plain">>, + listener => 'tcp:default', + protocol => mqtt + } + ). %%------------------------------------------------------------------------------ %% Helpers @@ -222,194 +252,218 @@ t_update(_Config) -> raw_redis_auth_config() -> #{ - mechanism => <<"password_based">>, - password_hash_algorithm => #{name => <<"plain">>, - salt_position => <<"suffix">>}, - enable => <<"true">>, + mechanism => <<"password_based">>, + password_hash_algorithm => #{ + name => <<"plain">>, + salt_position => <<"suffix">> + }, + enable => <<"true">>, - backend => <<"redis">>, - cmd => <<"HMGET mqtt_user:${username} password_hash salt is_superuser">>, - database => <<"1">>, - password => <<"public">>, - server => redis_server() - }. + backend => <<"redis">>, + cmd => <<"HMGET mqtt_user:${username} password_hash salt is_superuser">>, + database => <<"1">>, + password => <<"public">>, + server => redis_server() + }. user_seeds() -> - [#{data => #{ - password_hash => <<"plainsalt">>, - salt => <<"salt">>, - is_superuser => <<"1">> - }, - credentials => #{ - username => <<"plain">>, - password => <<"plain">>}, - key => <<"mqtt_user:plain">>, - config_params => #{}, - result => {ok,#{is_superuser => true}} - }, - - #{data => #{ - password_hash => <<"9b4d0c43d206d48279e69b9ad7132e22">>, - salt => <<"salt">>, - is_superuser => <<"0">> - }, - credentials => #{ - username => <<"md5">>, - password => <<"md5">> - }, - key => <<"mqtt_user:md5">>, - config_params => #{ - password_hash_algorithm => #{name => <<"md5">>, - salt_position => <<"suffix">>} - }, - result => {ok,#{is_superuser => false}} - }, - - #{data => #{ - password_hash => <<"ac63a624e7074776d677dd61a003b8c803eb11db004d0ec6ae032a5d7c9c5caf">>, - salt => <<"salt">>, - is_superuser => <<"1">> + [ + #{ + data => #{ + password_hash => <<"plainsalt">>, + salt => <<"salt">>, + is_superuser => <<"1">> + }, + credentials => #{ + username => <<"plain">>, + password => <<"plain">> + }, + key => <<"mqtt_user:plain">>, + config_params => #{}, + result => {ok, #{is_superuser => true}} }, - credentials => #{ - clientid => <<"sha256">>, - password => <<"sha256">> - }, - key => <<"mqtt_user:sha256">>, - config_params => #{ - cmd => <<"HMGET mqtt_user:${clientid} password_hash salt is_superuser">>, - password_hash_algorithm => #{name => <<"sha256">>, - salt_position => <<"prefix">>} - }, - result => {ok,#{is_superuser => true}} - }, - #{data => #{ - password_hash => - <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, - salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, - is_superuser => <<"0">> - }, - credentials => #{ - username => <<"bcrypt">>, - password => <<"bcrypt">> - }, - key => <<"mqtt_user:bcrypt">>, - config_params => #{ - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {ok,#{is_superuser => false}} - }, - #{data => #{ - password_hash => <<"01dbee7f4a9e243e988b62c73cda935da05378b9">>, - salt => <<"ATHENA.MIT.EDUraeburn">>, - is_superuser => <<"0">> - }, - credentials => #{ - username => <<"pbkdf2">>, - password => <<"password">> - }, - key => <<"mqtt_user:pbkdf2">>, - config_params => #{ - password_hash_algorithm => #{name => <<"pbkdf2">>, - iterations => 2, - mac_fun => sha - } - }, - result => {ok,#{is_superuser => false}} - }, - #{data => #{ - password_hash => - <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, - salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, - is_superuser => <<"0">> - }, - credentials => #{ - username => <<"bcrypt0">>, - password => <<"bcrypt">> - }, - key => <<"mqtt_user:bcrypt0">>, - config_params => #{ - % clientid variable & username credentials - cmd => <<"HMGET mqtt_client:${clientid} password_hash salt is_superuser">>, - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {error,not_authorized} - }, + #{ + data => #{ + password_hash => <<"9b4d0c43d206d48279e69b9ad7132e22">>, + salt => <<"salt">>, + is_superuser => <<"0">> + }, + credentials => #{ + username => <<"md5">>, + password => <<"md5">> + }, + key => <<"mqtt_user:md5">>, + config_params => #{ + password_hash_algorithm => #{ + name => <<"md5">>, + salt_position => <<"suffix">> + } + }, + result => {ok, #{is_superuser => false}} + }, - #{data => #{ - password_hash => - <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, - salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, - is_superuser => <<"0">> - }, - credentials => #{ - username => <<"bcrypt1">>, - password => <<"bcrypt">> - }, - key => <<"mqtt_user:bcrypt1">>, - config_params => #{ - % Bad key in cmd - cmd => <<"HMGET badkey:${username} password_hash salt is_superuser">>, - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {error,not_authorized} - }, + #{ + data => #{ + password_hash => + <<"ac63a624e7074776d677dd61a003b8c803eb11db004d0ec6ae032a5d7c9c5caf">>, + salt => <<"salt">>, + is_superuser => <<"1">> + }, + credentials => #{ + clientid => <<"sha256">>, + password => <<"sha256">> + }, + key => <<"mqtt_user:sha256">>, + config_params => #{ + cmd => <<"HMGET mqtt_user:${clientid} password_hash salt is_superuser">>, + password_hash_algorithm => #{ + name => <<"sha256">>, + salt_position => <<"prefix">> + } + }, + result => {ok, #{is_superuser => true}} + }, - #{data => #{ - password_hash => + #{ + data => #{ + password_hash => <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, - salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, - is_superuser => <<"0">> - }, - credentials => #{ - username => <<"bcrypt2">>, - % Wrong password - password => <<"wrongpass">> - }, - key => <<"mqtt_user:bcrypt2">>, - config_params => #{ - cmd => <<"HMGET mqtt_user:${username} password_hash salt is_superuser">>, - password_hash_algorithm => #{name => <<"bcrypt">>} - }, - result => {error,bad_username_or_password} - } + salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, + is_superuser => <<"0">> + }, + credentials => #{ + username => <<"bcrypt">>, + password => <<"bcrypt">> + }, + key => <<"mqtt_user:bcrypt">>, + config_params => #{ + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {ok, #{is_superuser => false}} + }, + #{ + data => #{ + password_hash => <<"01dbee7f4a9e243e988b62c73cda935da05378b9">>, + salt => <<"ATHENA.MIT.EDUraeburn">>, + is_superuser => <<"0">> + }, + credentials => #{ + username => <<"pbkdf2">>, + password => <<"password">> + }, + key => <<"mqtt_user:pbkdf2">>, + config_params => #{ + password_hash_algorithm => #{ + name => <<"pbkdf2">>, + iterations => 2, + mac_fun => sha + } + }, + result => {ok, #{is_superuser => false}} + }, + #{ + data => #{ + password_hash => + <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, + salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, + is_superuser => <<"0">> + }, + credentials => #{ + username => <<"bcrypt0">>, + password => <<"bcrypt">> + }, + key => <<"mqtt_user:bcrypt0">>, + config_params => #{ + % clientid variable & username credentials + cmd => <<"HMGET mqtt_client:${clientid} password_hash salt is_superuser">>, + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {error, not_authorized} + }, + + #{ + data => #{ + password_hash => + <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, + salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, + is_superuser => <<"0">> + }, + credentials => #{ + username => <<"bcrypt1">>, + password => <<"bcrypt">> + }, + key => <<"mqtt_user:bcrypt1">>, + config_params => #{ + % Bad key in cmd + cmd => <<"HMGET badkey:${username} password_hash salt is_superuser">>, + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {error, not_authorized} + }, + + #{ + data => #{ + password_hash => + <<"$2b$12$wtY3h20mUjjmeaClpqZVveDWGlHzCGsvuThMlneGHA7wVeFYyns2u">>, + salt => <<"$2b$12$wtY3h20mUjjmeaClpqZVve">>, + is_superuser => <<"0">> + }, + credentials => #{ + username => <<"bcrypt2">>, + % Wrong password + password => <<"wrongpass">> + }, + key => <<"mqtt_user:bcrypt2">>, + config_params => #{ + cmd => <<"HMGET mqtt_user:${username} password_hash salt is_superuser">>, + password_hash_algorithm => #{name => <<"bcrypt">>} + }, + result => {error, bad_username_or_password} + } ]. init_seeds() -> ok = drop_seeds(), lists:foreach( - fun(#{key := UserKey, data := Values}) -> - lists:foreach(fun({Key, Value}) -> - q(["HSET", UserKey, atom_to_list(Key), Value]) - end, - maps:to_list(Values)) - end, - user_seeds()). + fun(#{key := UserKey, data := Values}) -> + lists:foreach( + fun({Key, Value}) -> + q(["HSET", UserKey, atom_to_list(Key), Value]) + end, + maps:to_list(Values) + ) + end, + user_seeds() + ). q(Command) -> emqx_resource:query( - ?REDIS_RESOURCE, - {cmd, Command}). + ?REDIS_RESOURCE, + {cmd, Command} + ). drop_seeds() -> lists:foreach( - fun(#{key := UserKey}) -> - q(["DEL", UserKey]) - end, - user_seeds()). + fun(#{key := UserKey}) -> + q(["DEL", UserKey]) + end, + user_seeds() + ). redis_server() -> - iolist_to_binary(io_lib:format("~s",[?REDIS_HOST])). + iolist_to_binary(io_lib:format("~s", [?REDIS_HOST])). redis_config() -> - #{auto_reconnect => true, - database => 1, - pool_size => 8, - redis_type => single, - password => "public", - server => {?REDIS_HOST, ?REDIS_DEFAULT_PORT}, - ssl => #{enable => false} - }. + #{ + auto_reconnect => true, + database => 1, + pool_size => 8, + redis_type => single, + password => "public", + server => {?REDIS_HOST, ?REDIS_DEFAULT_PORT}, + ssl => #{enable => false} + }. start_apps(Apps) -> lists:foreach(fun application:ensure_all_started/1, Apps). diff --git a/apps/emqx_authn/test/emqx_authn_redis_tls_SUITE.erl b/apps/emqx_authn/test/emqx_authn_redis_tls_SUITE.erl index 177877e25..604495d8c 100644 --- a/apps/emqx_authn/test/emqx_authn_redis_tls_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_redis_tls_SUITE.erl @@ -39,8 +39,9 @@ init_per_testcase(_, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), emqx_authentication:initialize_authentication(?GLOBAL, []), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), Config. init_per_suite(Config) -> @@ -56,8 +57,9 @@ init_per_suite(Config) -> end_per_suite(_Config) -> emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), ok = stop_apps([emqx_resource, emqx_connector]), ok = emqx_common_test_helpers:stop_apps([emqx_authn]). @@ -67,39 +69,55 @@ end_per_suite(_Config) -> t_create(_Config) -> ?assertMatch( - {ok, _}, - create_redis_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.3">>], - <<"ciphers">> => [<<"TLS_CHACHA20_POLY1305_SHA256">>]})). + {ok, _}, + create_redis_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.3">>], + <<"ciphers">> => [<<"TLS_CHACHA20_POLY1305_SHA256">>] + } + ) + ). t_create_invalid(_Config) -> %% invalid server_name ?assertMatch( - {ok, _}, - create_redis_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server-unknown-host">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.3">>], - <<"ciphers">> => [<<"TLS_CHACHA20_POLY1305_SHA256">>]})), + {ok, _}, + create_redis_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server-unknown-host">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.3">>], + <<"ciphers">> => [<<"TLS_CHACHA20_POLY1305_SHA256">>] + } + ) + ), %% incompatible versions ?assertMatch( {error, _}, create_redis_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.1">>, <<"tlsv1.2">>]})), + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.1">>, <<"tlsv1.2">>] + } + ) + ), %% incompatible ciphers ?assertMatch( - {error, _}, - create_redis_auth_with_ssl_opts( - #{<<"server_name_indication">> => <<"authn-server">>, - <<"verify">> => <<"verify_peer">>, - <<"versions">> => [<<"tlsv1.3">>], - <<"ciphers">> => [<<"TLS_AES_128_GCM_SHA256">>]})). + {error, _}, + create_redis_auth_with_ssl_opts( + #{ + <<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.3">>], + <<"ciphers">> => [<<"TLS_AES_128_GCM_SHA256">>] + } + ) + ). %%------------------------------------------------------------------------------ %% Helpers @@ -111,24 +129,27 @@ create_redis_auth_with_ssl_opts(SpecificSSLOpts) -> raw_redis_auth_config(SpecificSSLOpts) -> SSLOpts = maps:merge( - emqx_authn_test_lib:client_ssl_cert_opts(), - #{enable => <<"true">>}), + emqx_authn_test_lib:client_ssl_cert_opts(), + #{enable => <<"true">>} + ), #{ - mechanism => <<"password_based">>, - password_hash_algorithm => #{name => <<"plain">>, - salt_position => <<"suffix">>}, - enable => <<"true">>, + mechanism => <<"password_based">>, + password_hash_algorithm => #{ + name => <<"plain">>, + salt_position => <<"suffix">> + }, + enable => <<"true">>, - backend => <<"redis">>, - cmd => <<"HMGET mqtt_user:${username} password_hash salt is_superuser">>, - database => <<"1">>, - password => <<"public">>, - server => redis_server(), - ssl => maps:merge(SSLOpts, SpecificSSLOpts) - }. + backend => <<"redis">>, + cmd => <<"HMGET mqtt_user:${username} password_hash salt is_superuser">>, + database => <<"1">>, + password => <<"public">>, + server => redis_server(), + ssl => maps:merge(SSLOpts, SpecificSSLOpts) + }. redis_server() -> - iolist_to_binary(io_lib:format("~s:~b",[?REDIS_HOST, ?REDIS_TLS_PORT])). + iolist_to_binary(io_lib:format("~s:~b", [?REDIS_HOST, ?REDIS_TLS_PORT])). start_apps(Apps) -> lists:foreach(fun application:ensure_all_started/1, Apps). diff --git a/apps/emqx_authn/test/emqx_authn_test_lib.erl b/apps/emqx_authn/test/emqx_authn_test_lib.erl index affa4aa77..9e162b718 100644 --- a/apps/emqx_authn/test/emqx_authn_test_lib.erl +++ b/apps/emqx_authn/test/emqx_authn_test_lib.erl @@ -36,16 +36,19 @@ jwt_example() -> delete_authenticators(Path, Chain) -> case emqx_authentication:list_authenticators(Chain) of - {error, _} -> ok; + {error, _} -> + ok; {ok, Authenticators} -> lists:foreach( fun(#{id := ID}) -> emqx:update_config( Path, {delete_authenticator, Chain, ID}, - #{rawconf_with_defaults => true}) + #{rawconf_with_defaults => true} + ) end, - Authenticators) + Authenticators + ) end. delete_config(ID) -> @@ -53,10 +56,13 @@ delete_config(ID) -> emqx:update_config( [authentication], {delete_authenticator, ?GLOBAL, ID}, - #{rawconf_with_defaults => false}). + #{rawconf_with_defaults => false} + ). client_ssl_cert_opts() -> Dir = code:lib_dir(emqx_authn, test), - #{keyfile => filename:join([Dir, "data/certs", "client.key"]), - certfile => filename:join([Dir, "data/certs", "client.crt"]), - cacertfile => filename:join([Dir, "data/certs", "ca.crt"])}. + #{ + keyfile => filename:join([Dir, "data/certs", "client.key"]), + certfile => filename:join([Dir, "data/certs", "client.crt"]), + cacertfile => filename:join([Dir, "data/certs", "ca.crt"]) + }. diff --git a/apps/emqx_authn/test/emqx_enhanced_authn_scram_mnesia_SUITE.erl b/apps/emqx_authn/test/emqx_enhanced_authn_scram_mnesia_SUITE.erl index b36b8ea56..8e1963791 100644 --- a/apps/emqx_authn/test/emqx_enhanced_authn_scram_mnesia_SUITE.erl +++ b/apps/emqx_authn/test/emqx_enhanced_authn_scram_mnesia_SUITE.erl @@ -26,14 +26,16 @@ -define(PATH, [authentication]). --define(USER_MAP, #{user_id := _, - is_superuser := _}). +-define(USER_MAP, #{ + user_id := _, + is_superuser := _ +}). all() -> emqx_common_test_helpers:all(?MODULE). init_per_suite(Config) -> - _ = application:load(emqx_conf), + _ = application:load(emqx_conf), ok = emqx_common_test_helpers:start_apps([emqx_authn]), Config. @@ -44,8 +46,9 @@ init_per_testcase(_Case, Config) -> {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), mria:clear_table(emqx_enhanced_authn_scram_mnesia), emqx_authn_test_lib:delete_authenticators( - [authentication], - ?GLOBAL), + [authentication], + ?GLOBAL + ), Config. end_per_testcase(_Case, Config) -> @@ -64,23 +67,25 @@ t_create(_Config) -> }, {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, ValidConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, ValidConfig} + ), - {ok, [#{provider := emqx_enhanced_authn_scram_mnesia}]} - = emqx_authentication:list_authenticators(?GLOBAL). + {ok, [#{provider := emqx_enhanced_authn_scram_mnesia}]} = + emqx_authentication:list_authenticators(?GLOBAL). t_create_invalid(_Config) -> InvalidConfig = #{ - <<"mechanism">> => <<"scram">>, - <<"backend">> => <<"built_in_database">>, - <<"algorithm">> => <<"sha271828">>, - <<"iteration_count">> => <<"4096">> - }, + <<"mechanism">> => <<"scram">>, + <<"backend">> => <<"built_in_database">>, + <<"algorithm">> => <<"sha271828">>, + <<"iteration_count">> => <<"4096">> + }, {error, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, InvalidConfig}), + ?PATH, + {create_authenticator, ?GLOBAL, InvalidConfig} + ), {ok, []} = emqx_authentication:list_authenticators(?GLOBAL). @@ -96,39 +101,47 @@ t_authenticate(_Config) -> ClientFirstMessage = esasl_scram:client_first_message(Username), ConnectPacket = ?CONNECT_PACKET( - #mqtt_packet_connect{ - proto_ver = ?MQTT_PROTO_V5, - properties = #{ - 'Authentication-Method' => <<"SCRAM-SHA-512">>, - 'Authentication-Data' => ClientFirstMessage - } - }), + #mqtt_packet_connect{ + proto_ver = ?MQTT_PROTO_V5, + properties = #{ + 'Authentication-Method' => <<"SCRAM-SHA-512">>, + 'Authentication-Data' => ClientFirstMessage + } + } + ), ok = emqx_authn_mqtt_test_client:send(Pid, ConnectPacket), ?AUTH_PACKET( - ?RC_CONTINUE_AUTHENTICATION, - #{'Authentication-Data' := ServerFirstMessage}) = receive_packet(), + ?RC_CONTINUE_AUTHENTICATION, + #{'Authentication-Data' := ServerFirstMessage} + ) = receive_packet(), {continue, ClientFinalMessage, ClientCache} = esasl_scram:check_server_first_message( ServerFirstMessage, - #{client_first_message => ClientFirstMessage, - password => Password, - algorithm => Algorithm} + #{ + client_first_message => ClientFirstMessage, + password => Password, + algorithm => Algorithm + } ), AuthContinuePacket = ?AUTH_PACKET( - ?RC_CONTINUE_AUTHENTICATION, - #{'Authentication-Method' => <<"SCRAM-SHA-512">>, - 'Authentication-Data' => ClientFinalMessage}), + ?RC_CONTINUE_AUTHENTICATION, + #{ + 'Authentication-Method' => <<"SCRAM-SHA-512">>, + 'Authentication-Data' => ClientFinalMessage + } + ), ok = emqx_authn_mqtt_test_client:send(Pid, AuthContinuePacket), ?CONNACK_PACKET( - ?RC_SUCCESS, - _, - #{'Authentication-Data' := ServerFinalMessage}) = receive_packet(), + ?RC_SUCCESS, + _, + #{'Authentication-Data' := ServerFinalMessage} + ) = receive_packet(), ok = esasl_scram:check_server_final_message( ServerFinalMessage, ClientCache#{algorithm => Algorithm} @@ -146,13 +159,14 @@ t_authenticate_bad_username(_Config) -> ClientFirstMessage = esasl_scram:client_first_message(<<"badusername">>), ConnectPacket = ?CONNECT_PACKET( - #mqtt_packet_connect{ - proto_ver = ?MQTT_PROTO_V5, - properties = #{ - 'Authentication-Method' => <<"SCRAM-SHA-512">>, - 'Authentication-Data' => ClientFirstMessage - } - }), + #mqtt_packet_connect{ + proto_ver = ?MQTT_PROTO_V5, + properties = #{ + 'Authentication-Method' => <<"SCRAM-SHA-512">>, + 'Authentication-Data' => ClientFirstMessage + } + } + ), ok = emqx_authn_mqtt_test_client:send(Pid, ConnectPacket), @@ -170,32 +184,39 @@ t_authenticate_bad_password(_Config) -> ClientFirstMessage = esasl_scram:client_first_message(Username), ConnectPacket = ?CONNECT_PACKET( - #mqtt_packet_connect{ - proto_ver = ?MQTT_PROTO_V5, - properties = #{ - 'Authentication-Method' => <<"SCRAM-SHA-512">>, - 'Authentication-Data' => ClientFirstMessage - } - }), + #mqtt_packet_connect{ + proto_ver = ?MQTT_PROTO_V5, + properties = #{ + 'Authentication-Method' => <<"SCRAM-SHA-512">>, + 'Authentication-Data' => ClientFirstMessage + } + } + ), ok = emqx_authn_mqtt_test_client:send(Pid, ConnectPacket), ?AUTH_PACKET( - ?RC_CONTINUE_AUTHENTICATION, - #{'Authentication-Data' := ServerFirstMessage}) = receive_packet(), + ?RC_CONTINUE_AUTHENTICATION, + #{'Authentication-Data' := ServerFirstMessage} + ) = receive_packet(), {continue, ClientFinalMessage, _ClientCache} = esasl_scram:check_server_first_message( ServerFirstMessage, - #{client_first_message => ClientFirstMessage, - password => <<"badpassword">>, - algorithm => Algorithm} + #{ + client_first_message => ClientFirstMessage, + password => <<"badpassword">>, + algorithm => Algorithm + } ), AuthContinuePacket = ?AUTH_PACKET( - ?RC_CONTINUE_AUTHENTICATION, - #{'Authentication-Method' => <<"SCRAM-SHA-512">>, - 'Authentication-Data' => ClientFinalMessage}), + ?RC_CONTINUE_AUTHENTICATION, + #{ + 'Authentication-Method' => <<"SCRAM-SHA-512">>, + 'Authentication-Data' => ClientFinalMessage + } + ), ok = emqx_authn_mqtt_test_client:send(Pid, AuthContinuePacket), @@ -218,7 +239,7 @@ t_destroy(_) -> ok = emqx_enhanced_authn_scram_mnesia:destroy(State0), {ok, State1} = emqx_enhanced_authn_scram_mnesia:create(<<"id">>, Config), - {error,not_found} = emqx_enhanced_authn_scram_mnesia:lookup_user(<<"u">>, State1), + {error, not_found} = emqx_enhanced_authn_scram_mnesia:lookup_user(<<"u">>, State1), {ok, _} = emqx_enhanced_authn_scram_mnesia:lookup_user(<<"u">>, StateOther). t_add_user(_) -> @@ -248,12 +269,14 @@ t_update_user(_) -> {ok, _} = emqx_enhanced_authn_scram_mnesia:add_user(User, State), {ok, #{is_superuser := false}} = emqx_enhanced_authn_scram_mnesia:lookup_user(<<"u">>, State), - {ok, - #{user_id := <<"u">>, - is_superuser := true}} = emqx_enhanced_authn_scram_mnesia:update_user( - <<"u">>, - #{password => <<"p1">>, is_superuser => true}, - State), + {ok, #{ + user_id := <<"u">>, + is_superuser := true + }} = emqx_enhanced_authn_scram_mnesia:update_user( + <<"u">>, + #{password => <<"p1">>, is_superuser => true}, + State + ), {ok, #{is_superuser := true}} = emqx_enhanced_authn_scram_mnesia:lookup_user(<<"u">>, State). @@ -261,29 +284,47 @@ t_list_users(_) -> Config = config(), {ok, State} = emqx_enhanced_authn_scram_mnesia:create(<<"id">>, Config), - Users = [#{user_id => <<"u1">>, password => <<"p">>}, - #{user_id => <<"u2">>, password => <<"p">>}, - #{user_id => <<"u3">>, password => <<"p">>}], + Users = [ + #{user_id => <<"u1">>, password => <<"p">>}, + #{user_id => <<"u2">>, password => <<"p">>}, + #{user_id => <<"u3">>, password => <<"p">>} + ], lists:foreach( - fun(U) -> {ok, _} = emqx_enhanced_authn_scram_mnesia:add_user(U, State) end, - Users), + fun(U) -> {ok, _} = emqx_enhanced_authn_scram_mnesia:add_user(U, State) end, + Users + ), - #{data := [?USER_MAP, ?USER_MAP], - meta := #{page := 1, limit := 2, count := 3}} = emqx_enhanced_authn_scram_mnesia:list_users( - #{<<"page">> => 1, <<"limit">> => 2}, - State), - #{data := [?USER_MAP], - meta := #{page := 2, limit := 2, count := 3}} = emqx_enhanced_authn_scram_mnesia:list_users( - #{<<"page">> => 2, <<"limit">> => 2}, - State), - #{data := [#{user_id := <<"u1">>, - is_superuser := _}], - meta := #{page := 1, limit := 3, count := 1}} = emqx_enhanced_authn_scram_mnesia:list_users( - #{ <<"page">> => 1 - , <<"limit">> => 3 - , <<"like_username">> => <<"1">>}, - State). + #{ + data := [?USER_MAP, ?USER_MAP], + meta := #{page := 1, limit := 2, count := 3} + } = emqx_enhanced_authn_scram_mnesia:list_users( + #{<<"page">> => 1, <<"limit">> => 2}, + State + ), + #{ + data := [?USER_MAP], + meta := #{page := 2, limit := 2, count := 3} + } = emqx_enhanced_authn_scram_mnesia:list_users( + #{<<"page">> => 2, <<"limit">> => 2}, + State + ), + #{ + data := [ + #{ + user_id := <<"u1">>, + is_superuser := _ + } + ], + meta := #{page := 1, limit := 3, count := 1} + } = emqx_enhanced_authn_scram_mnesia:list_users( + #{ + <<"page">> => 1, + <<"limit">> => 3, + <<"like_username">> => <<"1">> + }, + State + ). t_is_superuser(_Config) -> ok = test_is_superuser(#{is_superuser => false}, false), @@ -297,36 +338,44 @@ test_is_superuser(UserInfo, ExpectedIsSuperuser) -> Username = <<"u">>, Password = <<"p">>, - UserInfo0 = UserInfo#{user_id => Username, - password => Password}, + UserInfo0 = UserInfo#{ + user_id => Username, + password => Password + }, {ok, _} = emqx_enhanced_authn_scram_mnesia:add_user(UserInfo0, State), ClientFirstMessage = esasl_scram:client_first_message(Username), - {continue, ServerFirstMessage, ServerCache} - = emqx_enhanced_authn_scram_mnesia:authenticate( - #{auth_method => <<"SCRAM-SHA-512">>, - auth_data => ClientFirstMessage, - auth_cache => #{} - }, - State), + {continue, ServerFirstMessage, ServerCache} = + emqx_enhanced_authn_scram_mnesia:authenticate( + #{ + auth_method => <<"SCRAM-SHA-512">>, + auth_data => ClientFirstMessage, + auth_cache => #{} + }, + State + ), {continue, ClientFinalMessage, ClientCache} = esasl_scram:check_server_first_message( ServerFirstMessage, - #{client_first_message => ClientFirstMessage, - password => Password, - algorithm => sha512} + #{ + client_first_message => ClientFirstMessage, + password => Password, + algorithm => sha512 + } ), - {ok, UserInfo1, ServerFinalMessage} - = emqx_enhanced_authn_scram_mnesia:authenticate( - #{auth_method => <<"SCRAM-SHA-512">>, - auth_data => ClientFinalMessage, - auth_cache => ServerCache - }, - State), + {ok, UserInfo1, ServerFinalMessage} = + emqx_enhanced_authn_scram_mnesia:authenticate( + #{ + auth_method => <<"SCRAM-SHA-512">>, + auth_data => ClientFinalMessage, + auth_cache => ServerCache + }, + State + ), ok = esasl_scram:check_server_final_message( ServerFinalMessage, ClientCache#{algorithm => sha512} @@ -336,18 +385,17 @@ test_is_superuser(UserInfo, ExpectedIsSuperuser) -> ok = emqx_enhanced_authn_scram_mnesia:destroy(State). - %%------------------------------------------------------------------------------ %% Helpers %%------------------------------------------------------------------------------ config() -> #{ - mechanism => <<"scram">>, - backend => <<"built_in_database">>, - algorithm => sha512, - iteration_count => 4096 - }. + mechanism => <<"scram">>, + backend => <<"built_in_database">>, + algorithm => sha512, + iteration_count => 4096 + }. raw_config(Algorithm) -> #{ @@ -361,14 +409,16 @@ init_auth(Username, Password, Algorithm) -> Config = raw_config(Algorithm), {ok, _} = emqx:update_config( - ?PATH, - {create_authenticator, ?GLOBAL, Config}), + ?PATH, + {create_authenticator, ?GLOBAL, Config} + ), {ok, [#{state := State}]} = emqx_authentication:list_authenticators(?GLOBAL), emqx_enhanced_authn_scram_mnesia:add_user( - #{user_id => Username, password => Password}, - State). + #{user_id => Username, password => Password}, + State + ). receive_packet() -> receive