Merge remote-tracking branch 'ce/master' into authn-import-users-request
This commit is contained in:
commit
96f58d0ec8
|
@ -52,6 +52,7 @@
|
||||||
-type update_request() ::
|
-type update_request() ::
|
||||||
{create_authenticator, chain_name(), map()}
|
{create_authenticator, chain_name(), map()}
|
||||||
| {delete_authenticator, chain_name(), authenticator_id()}
|
| {delete_authenticator, chain_name(), authenticator_id()}
|
||||||
|
| {delete_authenticators, chain_name()}
|
||||||
| {update_authenticator, chain_name(), authenticator_id(), map()}
|
| {update_authenticator, chain_name(), authenticator_id(), map()}
|
||||||
| {move_authenticator, chain_name(), authenticator_id(), position()}.
|
| {move_authenticator, chain_name(), authenticator_id(), position()}.
|
||||||
|
|
||||||
|
@ -88,6 +89,8 @@ do_pre_config_update({delete_authenticator, _ChainName, AuthenticatorID}, OldCon
|
||||||
OldConfig
|
OldConfig
|
||||||
),
|
),
|
||||||
{ok, NewConfig};
|
{ok, NewConfig};
|
||||||
|
do_pre_config_update({delete_authenticators, _ChainName}, _OldConfig) ->
|
||||||
|
{ok, []};
|
||||||
do_pre_config_update({update_authenticator, ChainName, AuthenticatorID, Config}, OldConfig) ->
|
do_pre_config_update({update_authenticator, ChainName, AuthenticatorID, Config}, OldConfig) ->
|
||||||
CertsDir = certs_dir(ChainName, AuthenticatorID),
|
CertsDir = certs_dir(ChainName, AuthenticatorID),
|
||||||
NewConfig = lists:map(
|
NewConfig = lists:map(
|
||||||
|
@ -156,6 +159,25 @@ do_post_config_update(
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
{error, Reason}
|
{error, Reason}
|
||||||
end;
|
end;
|
||||||
|
do_post_config_update(
|
||||||
|
{delete_authenticators, ChainName},
|
||||||
|
_NewConfig,
|
||||||
|
OldConfig,
|
||||||
|
_AppEnvs
|
||||||
|
) ->
|
||||||
|
case emqx_authentication:delete_chain(ChainName) of
|
||||||
|
ok ->
|
||||||
|
lists:foreach(
|
||||||
|
fun(Config) ->
|
||||||
|
AuthenticatorID = authenticator_id(Config),
|
||||||
|
CertsDir = certs_dir(ChainName, AuthenticatorID),
|
||||||
|
ok = clear_certs(CertsDir, Config)
|
||||||
|
end,
|
||||||
|
to_list(OldConfig)
|
||||||
|
);
|
||||||
|
{error, Reason} ->
|
||||||
|
{error, Reason}
|
||||||
|
end;
|
||||||
do_post_config_update(
|
do_post_config_update(
|
||||||
{update_authenticator, ChainName, AuthenticatorID, Config},
|
{update_authenticator, ChainName, AuthenticatorID, Config},
|
||||||
NewConfig,
|
NewConfig,
|
||||||
|
|
|
@ -49,6 +49,13 @@ emqx_authn_api {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listeners_listener_id_authentication_delete {
|
||||||
|
desc {
|
||||||
|
en: """Delete listener-specific authentication."""
|
||||||
|
zh: """删除特定于侦听器的身份验证。"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
listeners_listener_id_authentication_post {
|
listeners_listener_id_authentication_post {
|
||||||
desc {
|
desc {
|
||||||
en: """Create authenticator for listener authentication."""
|
en: """Create authenticator for listener authentication."""
|
||||||
|
|
|
@ -257,6 +257,15 @@ schema("/listeners/:listener_id/authentication") ->
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
delete => #{
|
||||||
|
tags => ?API_TAGS_SINGLE,
|
||||||
|
description => ?DESC(listeners_listener_id_authentication_delete),
|
||||||
|
parameters => [param_listener_id()],
|
||||||
|
responses => #{
|
||||||
|
204 => <<"Authentication chain deleted">>,
|
||||||
|
404 => error_codes([?NOT_FOUND], <<"Not Found">>)
|
||||||
|
}
|
||||||
|
},
|
||||||
post => #{
|
post => #{
|
||||||
tags => ?API_TAGS_SINGLE,
|
tags => ?API_TAGS_SINGLE,
|
||||||
description => ?DESC(listeners_listener_id_authentication_post),
|
description => ?DESC(listeners_listener_id_authentication_post),
|
||||||
|
@ -646,6 +655,13 @@ listener_authenticators(get, #{bindings := #{listener_id := ListenerID}}) ->
|
||||||
fun(Type, Name, _) ->
|
fun(Type, Name, _) ->
|
||||||
list_authenticators([listeners, Type, Name, authentication])
|
list_authenticators([listeners, Type, Name, authentication])
|
||||||
end
|
end
|
||||||
|
);
|
||||||
|
listener_authenticators(delete, #{bindings := #{listener_id := ListenerID}}) ->
|
||||||
|
with_listener(
|
||||||
|
ListenerID,
|
||||||
|
fun(Type, Name, ChainName) ->
|
||||||
|
delete_authenticators([listeners, Type, Name, authentication], ChainName)
|
||||||
|
end
|
||||||
).
|
).
|
||||||
|
|
||||||
listener_authenticator(get, #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}}) ->
|
listener_authenticator(get, #{bindings := #{listener_id := ListenerID, id := AuthenticatorID}}) ->
|
||||||
|
@ -1098,6 +1114,16 @@ delete_authenticator(ConfKeyPath, ChainName, AuthenticatorID) ->
|
||||||
serialize_error(Reason)
|
serialize_error(Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
delete_authenticators(ConfKeyPath, ChainName) ->
|
||||||
|
case update_config(ConfKeyPath, {delete_authenticators, ChainName}) of
|
||||||
|
{ok, _} ->
|
||||||
|
{204};
|
||||||
|
{error, {_PrePostConfigUpdate, emqx_authentication, Reason}} ->
|
||||||
|
serialize_error(Reason);
|
||||||
|
{error, Reason} ->
|
||||||
|
serialize_error(Reason)
|
||||||
|
end.
|
||||||
|
|
||||||
move_authenticator(ConfKeyPath, ChainName, AuthenticatorID, Position) ->
|
move_authenticator(ConfKeyPath, ChainName, AuthenticatorID, Position) ->
|
||||||
case parse_position(Position) of
|
case parse_position(Position) of
|
||||||
{ok, NPosition} ->
|
{ok, NPosition} ->
|
||||||
|
|
|
@ -693,6 +693,84 @@ test_authenticator_upload_users(PathPrefix) ->
|
||||||
{filename, "user-credentials.csv", CSVData}
|
{filename, "user-credentials.csv", CSVData}
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
t_switch_to_global_chain(_) ->
|
||||||
|
{ok, 200, _} = request(
|
||||||
|
post,
|
||||||
|
uri([?CONF_NS]),
|
||||||
|
emqx_authn_test_lib:built_in_database_example()
|
||||||
|
),
|
||||||
|
|
||||||
|
{ok, 200, _} = request(
|
||||||
|
post,
|
||||||
|
uri([listeners, "tcp:default", ?CONF_NS]),
|
||||||
|
emqx_authn_test_lib:built_in_database_example()
|
||||||
|
),
|
||||||
|
|
||||||
|
GlobalUser = #{user_id => <<"global_user">>, password => <<"p1">>},
|
||||||
|
|
||||||
|
{ok, 201, _} = request(
|
||||||
|
post,
|
||||||
|
uri([?CONF_NS, "password_based:built_in_database", "users"]),
|
||||||
|
GlobalUser
|
||||||
|
),
|
||||||
|
|
||||||
|
ListenerUser = #{user_id => <<"listener_user">>, password => <<"p1">>},
|
||||||
|
|
||||||
|
{ok, 201, _} = request(
|
||||||
|
post,
|
||||||
|
uri([listeners, "tcp:default", ?CONF_NS, "password_based:built_in_database", "users"]),
|
||||||
|
ListenerUser
|
||||||
|
),
|
||||||
|
|
||||||
|
process_flag(trap_exit, true),
|
||||||
|
|
||||||
|
%% Listener user should be OK
|
||||||
|
{ok, Client0} = emqtt:start_link([
|
||||||
|
{username, <<"listener_user">>},
|
||||||
|
{password, <<"p1">>}
|
||||||
|
]),
|
||||||
|
?assertMatch(
|
||||||
|
{ok, _},
|
||||||
|
emqtt:connect(Client0)
|
||||||
|
),
|
||||||
|
ok = emqtt:disconnect(Client0),
|
||||||
|
|
||||||
|
%% Global user should not be OK
|
||||||
|
{ok, Client1} = emqtt:start_link([
|
||||||
|
{username, <<"global_user">>},
|
||||||
|
{password, <<"p1">>}
|
||||||
|
]),
|
||||||
|
?assertMatch(
|
||||||
|
{error, {unauthorized_client, _}},
|
||||||
|
emqtt:connect(Client1)
|
||||||
|
),
|
||||||
|
|
||||||
|
{ok, 204, _} = request(
|
||||||
|
delete,
|
||||||
|
uri([listeners, "tcp:default", ?CONF_NS])
|
||||||
|
),
|
||||||
|
|
||||||
|
%% Listener user should not be OK — local chain removed
|
||||||
|
{ok, Client2} = emqtt:start_link([
|
||||||
|
{username, <<"listener_user">>},
|
||||||
|
{password, <<"p1">>}
|
||||||
|
]),
|
||||||
|
?assertMatch(
|
||||||
|
{error, {unauthorized_client, _}},
|
||||||
|
emqtt:connect(Client2)
|
||||||
|
),
|
||||||
|
|
||||||
|
%% Global user should be now OK, switched back to the global chain
|
||||||
|
{ok, Client3} = emqtt:start_link([
|
||||||
|
{username, <<"global_user">>},
|
||||||
|
{password, <<"p1">>}
|
||||||
|
]),
|
||||||
|
?assertMatch(
|
||||||
|
{ok, _},
|
||||||
|
emqtt:connect(Client3)
|
||||||
|
),
|
||||||
|
ok = emqtt:disconnect(Client3).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Helpers
|
%% Helpers
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
|
@ -512,11 +512,5 @@ get_retry_ms() ->
|
||||||
|
|
||||||
maybe_init_tnx_id(_Node, TnxId) when TnxId < 0 -> ok;
|
maybe_init_tnx_id(_Node, TnxId) when TnxId < 0 -> ok;
|
||||||
maybe_init_tnx_id(Node, TnxId) ->
|
maybe_init_tnx_id(Node, TnxId) ->
|
||||||
{atomic, _} = transaction(fun init_node_tnx_id/2, [Node, TnxId]),
|
{atomic, _} = transaction(fun commit/2, [Node, TnxId]),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
init_node_tnx_id(Node, TnxId) ->
|
|
||||||
case mnesia:read(?CLUSTER_COMMIT, Node) of
|
|
||||||
[] -> commit(Node, TnxId);
|
|
||||||
_ -> ok
|
|
||||||
end.
|
|
||||||
|
|
|
@ -352,6 +352,8 @@ fields(Gw) when
|
||||||
->
|
->
|
||||||
[{name, mk(Gw, #{desc => ?DESC(gateway_name)})}] ++
|
[{name, mk(Gw, #{desc => ?DESC(gateway_name)})}] ++
|
||||||
convert_listener_struct(emqx_gateway_schema:fields(Gw));
|
convert_listener_struct(emqx_gateway_schema:fields(Gw));
|
||||||
|
fields(update_disable_enable_only) ->
|
||||||
|
[{enable, mk(boolean(), #{desc => <<"Enable/Disable the gateway">>})}];
|
||||||
fields(Gw) when
|
fields(Gw) when
|
||||||
Gw == update_stomp;
|
Gw == update_stomp;
|
||||||
Gw == update_mqttsn;
|
Gw == update_mqttsn;
|
||||||
|
@ -411,7 +413,8 @@ schema_update_gateways_conf() ->
|
||||||
ref(?MODULE, update_mqttsn),
|
ref(?MODULE, update_mqttsn),
|
||||||
ref(?MODULE, update_coap),
|
ref(?MODULE, update_coap),
|
||||||
ref(?MODULE, update_lwm2m),
|
ref(?MODULE, update_lwm2m),
|
||||||
ref(?MODULE, update_exproto)
|
ref(?MODULE, update_exproto),
|
||||||
|
ref(?MODULE, update_disable_enable_only)
|
||||||
]),
|
]),
|
||||||
examples_update_gateway_confs()
|
examples_update_gateway_confs()
|
||||||
).
|
).
|
||||||
|
|
|
@ -73,6 +73,8 @@
|
||||||
-type map_or_err() :: {ok, map()} | {error, term()}.
|
-type map_or_err() :: {ok, map()} | {error, term()}.
|
||||||
-type listener_ref() :: {ListenerType :: atom_or_bin(), ListenerName :: atom_or_bin()}.
|
-type listener_ref() :: {ListenerType :: atom_or_bin(), ListenerName :: atom_or_bin()}.
|
||||||
|
|
||||||
|
-define(IS_SSL(T), (T == <<"ssl">> orelse T == <<"dtls">>)).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Load/Unload
|
%% Load/Unload
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -403,10 +405,7 @@ pre_config_update(_, {update_gateway, GwName, Conf}, RawConf) ->
|
||||||
GwRawConf ->
|
GwRawConf ->
|
||||||
Conf1 = maps:without([<<"listeners">>, ?AUTHN_BIN], Conf),
|
Conf1 = maps:without([<<"listeners">>, ?AUTHN_BIN], Conf),
|
||||||
NConf = tune_gw_certs(fun convert_certs/2, GwName, Conf1),
|
NConf = tune_gw_certs(fun convert_certs/2, GwName, Conf1),
|
||||||
NConf1 = maps:merge(
|
NConf1 = maps:merge(GwRawConf, NConf),
|
||||||
maps:with([<<"listeners">>, ?AUTHN_BIN], GwRawConf),
|
|
||||||
NConf
|
|
||||||
),
|
|
||||||
{ok, emqx_map_lib:deep_put([GwName], RawConf, NConf1)}
|
{ok, emqx_map_lib:deep_put([GwName], RawConf, NConf1)}
|
||||||
end;
|
end;
|
||||||
pre_config_update(_, {unload_gateway, GwName}, RawConf) ->
|
pre_config_update(_, {unload_gateway, GwName}, RawConf) ->
|
||||||
|
@ -680,9 +679,17 @@ apply_to_listeners(Fun, GwName, Conf) ->
|
||||||
apply_to_gateway_basic_confs(Fun, <<"exproto">>, Conf) ->
|
apply_to_gateway_basic_confs(Fun, <<"exproto">>, Conf) ->
|
||||||
SvrDir = filename:join(["exproto", "server"]),
|
SvrDir = filename:join(["exproto", "server"]),
|
||||||
HdrDir = filename:join(["exproto", "handler"]),
|
HdrDir = filename:join(["exproto", "handler"]),
|
||||||
NServerConf = erlang:apply(Fun, [SvrDir, maps:get(<<"server">>, Conf, #{})]),
|
Conf1 =
|
||||||
NHandlerConf = erlang:apply(Fun, [HdrDir, maps:get(<<"handler">>, Conf, #{})]),
|
case maps:get(<<"server">>, Conf, undefined) of
|
||||||
maps:put(<<"handler">>, NHandlerConf, maps:put(<<"server">>, NServerConf, Conf));
|
undefined ->
|
||||||
|
Conf;
|
||||||
|
ServerConf ->
|
||||||
|
maps:put(<<"server">>, erlang:apply(Fun, [SvrDir, ServerConf]), Conf)
|
||||||
|
end,
|
||||||
|
case maps:get(<<"handler">>, Conf1, undefined) of
|
||||||
|
undefined -> Conf1;
|
||||||
|
HandlerConf -> maps:put(<<"handler">>, erlang:apply(Fun, [HdrDir, HandlerConf]), Conf1)
|
||||||
|
end;
|
||||||
apply_to_gateway_basic_confs(_Fun, _GwName, Conf) ->
|
apply_to_gateway_basic_confs(_Fun, _GwName, Conf) ->
|
||||||
Conf.
|
Conf.
|
||||||
|
|
||||||
|
@ -690,34 +697,43 @@ certs_dir(GwName) when is_binary(GwName) ->
|
||||||
GwName.
|
GwName.
|
||||||
|
|
||||||
convert_certs(SubDir, Conf) ->
|
convert_certs(SubDir, Conf) ->
|
||||||
|
convert_certs(<<"dtls">>, SubDir, convert_certs(<<"ssl">>, SubDir, Conf)).
|
||||||
|
|
||||||
|
convert_certs(Type, SubDir, Conf) when ?IS_SSL(Type) ->
|
||||||
case
|
case
|
||||||
emqx_tls_lib:ensure_ssl_files(
|
emqx_tls_lib:ensure_ssl_files(
|
||||||
SubDir,
|
SubDir,
|
||||||
maps:get(<<"ssl">>, Conf, undefined)
|
maps:get(Type, Conf, undefined)
|
||||||
)
|
)
|
||||||
of
|
of
|
||||||
{ok, SSL} ->
|
{ok, SSL} ->
|
||||||
new_ssl_config(Conf, SSL);
|
new_ssl_config(Type, Conf, SSL);
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?SLOG(error, Reason#{msg => bad_ssl_config}),
|
?SLOG(error, Reason#{msg => bad_ssl_config}),
|
||||||
throw({bad_ssl_config, Reason})
|
throw({bad_ssl_config, Reason})
|
||||||
end.
|
end;
|
||||||
|
convert_certs(SubDir, NConf, OConf) when is_map(NConf); is_map(OConf) ->
|
||||||
|
convert_certs(<<"dtls">>, SubDir, convert_certs(<<"ssl">>, SubDir, NConf, OConf), OConf).
|
||||||
|
|
||||||
convert_certs(SubDir, NConf, OConf) ->
|
convert_certs(Type, SubDir, NConf, OConf) when ?IS_SSL(Type) ->
|
||||||
OSSL = maps:get(<<"ssl">>, OConf, undefined),
|
OSSL = maps:get(Type, OConf, undefined),
|
||||||
NSSL = maps:get(<<"ssl">>, NConf, undefined),
|
NSSL = maps:get(Type, NConf, undefined),
|
||||||
case emqx_tls_lib:ensure_ssl_files(SubDir, NSSL) of
|
case emqx_tls_lib:ensure_ssl_files(SubDir, NSSL) of
|
||||||
{ok, NSSL1} ->
|
{ok, NSSL1} ->
|
||||||
ok = emqx_tls_lib:delete_ssl_files(SubDir, NSSL1, OSSL),
|
ok = emqx_tls_lib:delete_ssl_files(SubDir, NSSL1, OSSL),
|
||||||
new_ssl_config(NConf, NSSL1);
|
new_ssl_config(Type, NConf, NSSL1);
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?SLOG(error, Reason#{msg => bad_ssl_config}),
|
?SLOG(error, Reason#{msg => bad_ssl_config}),
|
||||||
throw({bad_ssl_config, Reason})
|
throw({bad_ssl_config, Reason})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
new_ssl_config(Conf, undefined) -> Conf;
|
new_ssl_config(_Type, Conf, undefined) -> Conf;
|
||||||
new_ssl_config(Conf, SSL) -> Conf#{<<"ssl">> => SSL}.
|
new_ssl_config(Type, Conf, SSL) when ?IS_SSL(Type) -> Conf#{Type => SSL}.
|
||||||
|
|
||||||
clear_certs(SubDir, Conf) ->
|
clear_certs(SubDir, Conf) ->
|
||||||
SSL = maps:get(<<"ssl">>, Conf, undefined),
|
clear_certs(<<"ssl">>, SubDir, Conf),
|
||||||
|
clear_certs(<<"dtls">>, SubDir, Conf).
|
||||||
|
|
||||||
|
clear_certs(Type, SubDir, Conf) when ?IS_SSL(Type) ->
|
||||||
|
SSL = maps:get(Type, Conf, undefined),
|
||||||
ok = emqx_tls_lib:delete_ssl_files(SubDir, undefined, SSL).
|
ok = emqx_tls_lib:delete_ssl_files(SubDir, undefined, SSL).
|
||||||
|
|
Loading…
Reference in New Issue