feat(authn hot config): initial support for hot config
This commit is contained in:
parent
ef59309ed0
commit
61da3a4fd7
|
@ -16,8 +16,17 @@
|
||||||
|
|
||||||
-module(emqx_authn).
|
-module(emqx_authn).
|
||||||
|
|
||||||
|
-behaviour(emqx_config_handler).
|
||||||
|
|
||||||
-include("emqx_authn.hrl").
|
-include("emqx_authn.hrl").
|
||||||
|
|
||||||
|
-export([mnesia/1]).
|
||||||
|
|
||||||
|
-export([ pre_config_update/2
|
||||||
|
, post_config_update/3
|
||||||
|
, update_config/2
|
||||||
|
]).
|
||||||
|
|
||||||
-export([ enable/0
|
-export([ enable/0
|
||||||
, disable/0
|
, disable/0
|
||||||
, is_enabled/0
|
, is_enabled/0
|
||||||
|
@ -46,8 +55,6 @@
|
||||||
, list_users/2
|
, list_users/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([mnesia/1]).
|
|
||||||
|
|
||||||
-boot_mnesia({mnesia, [boot]}).
|
-boot_mnesia({mnesia, [boot]}).
|
||||||
-copy_mnesia({mnesia, [copy]}).
|
-copy_mnesia({mnesia, [copy]}).
|
||||||
|
|
||||||
|
@ -75,6 +82,97 @@ mnesia(boot) ->
|
||||||
mnesia(copy) ->
|
mnesia(copy) ->
|
||||||
ok = ekka_mnesia:copy_table(?CHAIN_TAB, ram_copies).
|
ok = ekka_mnesia:copy_table(?CHAIN_TAB, ram_copies).
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% APIs
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
pre_config_update({enable, Enable}, _OldConfig) ->
|
||||||
|
Enable;
|
||||||
|
pre_config_update({create_authenticator, Config}, OldConfig) ->
|
||||||
|
OldConfig ++ [Config];
|
||||||
|
pre_config_update({delete_authenticator, ID}, OldConfig) ->
|
||||||
|
case lookup_authenticator(?CHAIN, ID) of
|
||||||
|
{error, Reason} -> error(Reason);
|
||||||
|
{ok, #{name := Name}} ->
|
||||||
|
lists:filter(fun(#{<<"name">> := N}) ->
|
||||||
|
N =/= Name
|
||||||
|
end, OldConfig)
|
||||||
|
end;
|
||||||
|
pre_config_update({update_authenticator, ID, Config}, OldConfig) ->
|
||||||
|
case lookup_authenticator(?CHAIN, ID) of
|
||||||
|
{error, Reason} -> error(Reason);
|
||||||
|
{ok, #{name := Name}} ->
|
||||||
|
lists:map(fun(#{<<"name">> := N} = C) ->
|
||||||
|
case N =:= Name of
|
||||||
|
true -> Config;
|
||||||
|
false -> C
|
||||||
|
end
|
||||||
|
end, OldConfig)
|
||||||
|
end;
|
||||||
|
pre_config_update({update_or_create_authenticator, ID, Config}, OldConfig) ->
|
||||||
|
case lookup_authenticator(?CHAIN, ID) of
|
||||||
|
{error, _Reason} -> OldConfig ++ [Config];
|
||||||
|
{ok, #{name := Name}} ->
|
||||||
|
lists:map(fun(#{<<"name">> := N} = C) ->
|
||||||
|
case N =:= Name of
|
||||||
|
true -> Config;
|
||||||
|
false -> C
|
||||||
|
end
|
||||||
|
end, OldConfig)
|
||||||
|
end.
|
||||||
|
|
||||||
|
post_config_update({enable, true}, _NewConfig, _OldConfig) ->
|
||||||
|
emqx_authn:enable();
|
||||||
|
post_config_update({enable, false}, _NewConfig, _OldConfig) ->
|
||||||
|
emqx_authn:disable();
|
||||||
|
post_config_update({create_authenticator, #{<<"name">> := Name}}, NewConfig, _OldConfig) ->
|
||||||
|
case lists:filter(
|
||||||
|
fun(#{name := N}) ->
|
||||||
|
N =:= Name
|
||||||
|
end, NewConfig) of
|
||||||
|
[Config] ->
|
||||||
|
case create_authenticator(?CHAIN, Config) of
|
||||||
|
{ok, _} -> ok;
|
||||||
|
{error, Reason} -> throw(Reason)
|
||||||
|
end;
|
||||||
|
[_Config | _] ->
|
||||||
|
error(name_has_be_used)
|
||||||
|
end;
|
||||||
|
post_config_update({delete_authenticator, ID}, _NewConfig, _OldConfig) ->
|
||||||
|
case delete_authenticator(?CHAIN, ID) of
|
||||||
|
ok -> ok;
|
||||||
|
{error, Reason} -> throw(Reason)
|
||||||
|
end;
|
||||||
|
post_config_update({update_authenticator, ID, #{<<"name">> := Name}}, NewConfig, _OldConfig) ->
|
||||||
|
case lists:filter(
|
||||||
|
fun(#{name := N}) ->
|
||||||
|
N =:= Name
|
||||||
|
end, NewConfig) of
|
||||||
|
[Config] ->
|
||||||
|
case update_authenticator(?CHAIN, ID, Config) of
|
||||||
|
{ok, _} -> ok;
|
||||||
|
{error, Reason} -> throw(Reason)
|
||||||
|
end;
|
||||||
|
[_Config | _] ->
|
||||||
|
error(name_has_be_used)
|
||||||
|
end;
|
||||||
|
post_config_update({update_or_create_authenticator, ID, #{<<"name">> := Name}}, NewConfig, _OldConfig) ->
|
||||||
|
case lists:filter(
|
||||||
|
fun(#{name := N}) ->
|
||||||
|
N =:= Name
|
||||||
|
end, NewConfig) of
|
||||||
|
[Config] ->
|
||||||
|
case update_or_create_authenticator(?CHAIN, ID, Config) of
|
||||||
|
{ok, _} -> ok;
|
||||||
|
{error, Reason} -> throw(Reason)
|
||||||
|
end;
|
||||||
|
[_Config | _] ->
|
||||||
|
error(name_has_be_used)
|
||||||
|
end.
|
||||||
|
|
||||||
|
update_config(Path, ConfigRequest) ->
|
||||||
|
emqx_config:update(emqx_authn_schema, Path, ConfigRequest).
|
||||||
|
|
||||||
enable() ->
|
enable() ->
|
||||||
case emqx:hook('client.authenticate', {?MODULE, authenticate, []}) of
|
case emqx:hook('client.authenticate', {?MODULE, authenticate, []}) of
|
||||||
ok -> ok;
|
ok -> ok;
|
||||||
|
|
|
@ -1253,14 +1253,9 @@ definitions() ->
|
||||||
authentication(post, Request) ->
|
authentication(post, Request) ->
|
||||||
{ok, Body, _} = cowboy_req:read_body(Request),
|
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||||
case emqx_json:decode(Body, [return_maps]) of
|
case emqx_json:decode(Body, [return_maps]) of
|
||||||
#{<<"enable">> := true} ->
|
#{<<"enable">> := Enable} ->
|
||||||
ok = emqx_authn:enable(),
|
emqx_authn:update_config([authentication, enable], {enable, Enable}),
|
||||||
{204};
|
{204};
|
||||||
#{<<"enable">> := false} ->
|
|
||||||
ok = emqx_authn:disable(),
|
|
||||||
{204};
|
|
||||||
#{<<"enable">> := _} ->
|
|
||||||
serialize_error({invalid_parameter, enable});
|
|
||||||
_ ->
|
_ ->
|
||||||
serialize_error({missing_parameter, enable})
|
serialize_error({missing_parameter, enable})
|
||||||
end;
|
end;
|
||||||
|
@ -1270,16 +1265,10 @@ authentication(get, _Request) ->
|
||||||
|
|
||||||
authenticators(post, Request) ->
|
authenticators(post, Request) ->
|
||||||
{ok, Body, _} = cowboy_req:read_body(Request),
|
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||||
AuthenticatorConfig = emqx_json:decode(Body, [return_maps]),
|
Config = emqx_json:decode(Body, [return_maps]),
|
||||||
Config = #{<<"authentication">> => #{
|
case emqx_authn:update_config([authentication, authenticators], {create_authenticator, Config}) of
|
||||||
<<"authenticators">> => [AuthenticatorConfig]
|
ok ->
|
||||||
}},
|
{204};
|
||||||
NConfig = hocon_schema:check_plain(emqx_authn_schema, Config,
|
|
||||||
#{nullable => true}),
|
|
||||||
#{authentication := #{authenticators := [NAuthenticatorConfig]}} = emqx_map_lib:unsafe_atom_key_map(NConfig),
|
|
||||||
case emqx_authn:create_authenticator(?CHAIN, NAuthenticatorConfig) of
|
|
||||||
{ok, Authenticator2} ->
|
|
||||||
{201, Authenticator2};
|
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
serialize_error(Reason)
|
serialize_error(Reason)
|
||||||
end;
|
end;
|
||||||
|
@ -1298,22 +1287,17 @@ authenticators2(get, Request) ->
|
||||||
authenticators2(put, Request) ->
|
authenticators2(put, Request) ->
|
||||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||||
{ok, Body, _} = cowboy_req:read_body(Request),
|
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||||
AuthenticatorConfig = emqx_json:decode(Body, [return_maps]),
|
Config = emqx_json:decode(Body, [return_maps]),
|
||||||
Config = #{<<"authentication">> => #{
|
case emqx_authn:update_config([authentication, authenticators],
|
||||||
<<"authenticators">> => [AuthenticatorConfig]
|
{update_or_create_authenticator, AuthenticatorID, Config}) of
|
||||||
}},
|
ok ->
|
||||||
NConfig = hocon_schema:check_plain(emqx_authn_schema, Config,
|
{204};
|
||||||
#{nullable => true}),
|
|
||||||
#{authentication := #{authenticators := [NAuthenticatorConfig]}} = emqx_map_lib:unsafe_atom_key_map(NConfig),
|
|
||||||
case emqx_authn:update_or_create_authenticator(?CHAIN, AuthenticatorID, NAuthenticatorConfig) of
|
|
||||||
{ok, Authenticator} ->
|
|
||||||
{200, Authenticator};
|
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
serialize_error(Reason)
|
serialize_error(Reason)
|
||||||
end;
|
end;
|
||||||
authenticators2(delete, Request) ->
|
authenticators2(delete, Request) ->
|
||||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||||
case emqx_authn:delete_authenticator(?CHAIN, AuthenticatorID) of
|
case emqx_authn:update_config([authentication, authenticators], {delete_authenticator, AuthenticatorID}) of
|
||||||
ok ->
|
ok ->
|
||||||
{204};
|
{204};
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
|
@ -1324,7 +1308,7 @@ position(post, Request) ->
|
||||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||||
{ok, Body, _} = cowboy_req:read_body(Request),
|
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||||
NBody = emqx_json:decode(Body, [return_maps]),
|
NBody = emqx_json:decode(Body, [return_maps]),
|
||||||
Config = hocon_schema:check_plain(emqx_authn_other_schema, #{<<"position">> => NBody},
|
Config = hocon_schema:check_plain(emqx_authn_implied_schema, #{<<"position">> => NBody},
|
||||||
#{nullable => true}, ["position"]),
|
#{nullable => true}, ["position"]),
|
||||||
#{position := #{position := Position}} = emqx_map_lib:unsafe_atom_key_map(Config),
|
#{position := #{position := Position}} = emqx_map_lib:unsafe_atom_key_map(Config),
|
||||||
case emqx_authn:move_authenticator_to_the_nth(?CHAIN, AuthenticatorID, Position) of
|
case emqx_authn:move_authenticator_to_the_nth(?CHAIN, AuthenticatorID, Position) of
|
||||||
|
@ -1338,7 +1322,7 @@ import_users(post, Request) ->
|
||||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||||
{ok, Body, _} = cowboy_req:read_body(Request),
|
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||||
NBody = emqx_json:decode(Body, [return_maps]),
|
NBody = emqx_json:decode(Body, [return_maps]),
|
||||||
Config = hocon_schema:check_plain(emqx_authn_other_schema, #{<<"filename">> => NBody},
|
Config = hocon_schema:check_plain(emqx_authn_implied_schema, #{<<"filename">> => NBody},
|
||||||
#{nullable => true}, ["filename"]),
|
#{nullable => true}, ["filename"]),
|
||||||
#{filename := #{filename := Filename}} = emqx_map_lib:unsafe_atom_key_map(Config),
|
#{filename := #{filename := Filename}} = emqx_map_lib:unsafe_atom_key_map(Config),
|
||||||
case emqx_authn:import_users(?CHAIN, AuthenticatorID, Filename) of
|
case emqx_authn:import_users(?CHAIN, AuthenticatorID, Filename) of
|
||||||
|
@ -1352,7 +1336,7 @@ users(post, Request) ->
|
||||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||||
{ok, Body, _} = cowboy_req:read_body(Request),
|
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||||
NBody = emqx_json:decode(Body, [return_maps]),
|
NBody = emqx_json:decode(Body, [return_maps]),
|
||||||
Config = hocon_schema:check_plain(emqx_authn_other_schema, #{<<"user_info">> => NBody},
|
Config = hocon_schema:check_plain(emqx_authn_implied_schema, #{<<"user_info">> => NBody},
|
||||||
#{nullable => true}, ["user_info"]),
|
#{nullable => true}, ["user_info"]),
|
||||||
#{user_info := UserInfo} = emqx_map_lib:unsafe_atom_key_map(Config),
|
#{user_info := UserInfo} = emqx_map_lib:unsafe_atom_key_map(Config),
|
||||||
case emqx_authn:add_user(?CHAIN, AuthenticatorID, UserInfo) of
|
case emqx_authn:add_user(?CHAIN, AuthenticatorID, UserInfo) of
|
||||||
|
@ -1375,7 +1359,7 @@ users2(patch, Request) ->
|
||||||
UserID = cowboy_req:binding(user_id, Request),
|
UserID = cowboy_req:binding(user_id, Request),
|
||||||
{ok, Body, _} = cowboy_req:read_body(Request),
|
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||||
NBody = emqx_json:decode(Body, [return_maps]),
|
NBody = emqx_json:decode(Body, [return_maps]),
|
||||||
Config = hocon_schema:check_plain(emqx_authn_other_schema, #{<<"new_user_info">> => NBody},
|
Config = hocon_schema:check_plain(emqx_authn_implied_schema, #{<<"new_user_info">> => NBody},
|
||||||
#{nullable => true}, ["new_user_info"]),
|
#{nullable => true}, ["new_user_info"]),
|
||||||
#{new_user_info := NewUserInfo} = emqx_map_lib:unsafe_atom_key_map(Config),
|
#{new_user_info := NewUserInfo} = emqx_map_lib:unsafe_atom_key_map(Config),
|
||||||
case emqx_authn:update_user(?CHAIN, AuthenticatorID, UserID, NewUserInfo) of
|
case emqx_authn:update_user(?CHAIN, AuthenticatorID, UserID, NewUserInfo) of
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
start(_StartType, _StartArgs) ->
|
start(_StartType, _StartArgs) ->
|
||||||
ok = ekka_rlog:wait_for_shards([?AUTH_SHARD], infinity),
|
ok = ekka_rlog:wait_for_shards([?AUTH_SHARD], infinity),
|
||||||
{ok, Sup} = emqx_authn_sup:start_link(),
|
{ok, Sup} = emqx_authn_sup:start_link(),
|
||||||
|
emqx_config_handler:add_handler([authentication, authenticators], emqx_authn),
|
||||||
initialize(),
|
initialize(),
|
||||||
{ok, Sup}.
|
{ok, Sup}.
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
%% limitations under the License.
|
%% limitations under the License.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
-module(emqx_authn_other_schema).
|
-module(emqx_authn_implied_schema).
|
||||||
|
|
||||||
-include("emqx_authn.hrl").
|
-include("emqx_authn.hrl").
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("typerefl/include/types.hrl").
|
Loading…
Reference in New Issue