feat(auth): support hot config
This commit is contained in:
parent
e6f9767066
commit
e5892d16e5
|
@ -26,7 +26,6 @@
|
|||
{ id :: binary()
|
||||
, name :: binary()
|
||||
, provider :: module()
|
||||
, config :: map()
|
||||
, state :: map()
|
||||
}).
|
||||
|
||||
|
|
|
@ -76,60 +76,63 @@
|
|||
%%------------------------------------------------------------------------------
|
||||
|
||||
pre_config_update({enable, Enable}, _OldConfig) ->
|
||||
Enable;
|
||||
{ok, Enable};
|
||||
pre_config_update({create_authenticator, Config}, OldConfig) ->
|
||||
OldConfig ++ [Config];
|
||||
{ok, OldConfig ++ [Config]};
|
||||
pre_config_update({delete_authenticator, ID}, OldConfig) ->
|
||||
case lookup_authenticator(?CHAIN, ID) of
|
||||
{error, Reason} -> error(Reason);
|
||||
{error, Reason} -> {error, Reason};
|
||||
{ok, #{name := Name}} ->
|
||||
lists:filter(fun(#{<<"name">> := N}) ->
|
||||
N =/= Name
|
||||
end, OldConfig)
|
||||
NewConfig = lists:filter(fun(#{<<"name">> := N}) ->
|
||||
N =/= Name
|
||||
end, OldConfig),
|
||||
{ok, NewConfig}
|
||||
end;
|
||||
pre_config_update({update_authenticator, ID, Config}, OldConfig) ->
|
||||
case lookup_authenticator(?CHAIN, ID) of
|
||||
{error, Reason} -> error(Reason);
|
||||
{error, Reason} -> {error, Reason};
|
||||
{ok, #{name := Name}} ->
|
||||
lists:map(fun(#{<<"name">> := N} = C) ->
|
||||
case N =:= Name of
|
||||
true -> Config;
|
||||
false -> C
|
||||
end
|
||||
end, OldConfig)
|
||||
NewConfig = lists:map(fun(#{<<"name">> := N} = C) ->
|
||||
case N =:= Name of
|
||||
true -> Config;
|
||||
false -> C
|
||||
end
|
||||
end, OldConfig),
|
||||
{ok, NewConfig}
|
||||
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)
|
||||
NewConfig = lists:map(fun(#{<<"name">> := N} = C) ->
|
||||
case N =:= Name of
|
||||
true -> Config;
|
||||
false -> C
|
||||
end
|
||||
end, OldConfig),
|
||||
{ok, NewConfig}
|
||||
end;
|
||||
pre_config_update({move, ID, Position}, OldConfig) ->
|
||||
pre_config_update({move_authenticator, ID, Position}, OldConfig) ->
|
||||
case lookup_authenticator(?CHAIN, ID) of
|
||||
{error, Reason} -> error(Reason);
|
||||
{error, Reason} -> {error, Reason};
|
||||
{ok, #{name := Name}} ->
|
||||
{ok, Found, Part1, Part2} = split_by_name(Name, OldConfig),
|
||||
case Position of
|
||||
<<"top">> ->
|
||||
[Found | Part1] ++ Part2;
|
||||
{ok, [Found | Part1] ++ Part2};
|
||||
<<"bottom">> ->
|
||||
Part1 ++ Part2 ++ [Found];
|
||||
{ok, Part1 ++ Part2 ++ [Found]};
|
||||
Before ->
|
||||
case binary:split(Before, <<":">>, [global]) of
|
||||
[<<"before">>, ID0] ->
|
||||
case lookup_authenticator(?CHAIN, ID0) of
|
||||
{error, Reason} -> error(Reason);
|
||||
{error, Reason} -> {error, Reason};
|
||||
{ok, #{name := Name1}} ->
|
||||
{ok, NFound, NPart1, NPart2} = split_by_name(Name1, Part1 + Part2),
|
||||
NPart1 ++ [Found, NFound | NPart2]
|
||||
{ok, NFound, NPart1, NPart2} = split_by_name(Name1, Part1 ++ Part2),
|
||||
{ok, NPart1 ++ [Found, NFound | NPart2]}
|
||||
end;
|
||||
_ ->
|
||||
error({invalid_parameter, position})
|
||||
{error, {invalid_parameter, position}}
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
@ -144,12 +147,9 @@ post_config_update({create_authenticator, #{<<"name">> := Name}}, NewConfig, _Ol
|
|||
N =:= Name
|
||||
end, NewConfig) of
|
||||
[Config] ->
|
||||
case create_authenticator(?CHAIN, Config) of
|
||||
{ok, _} -> ok;
|
||||
{error, Reason} -> throw(Reason)
|
||||
end;
|
||||
create_authenticator(?CHAIN, Config);
|
||||
[_Config | _] ->
|
||||
error(name_has_be_used)
|
||||
{error, name_has_be_used}
|
||||
end;
|
||||
post_config_update({delete_authenticator, ID}, _NewConfig, _OldConfig) ->
|
||||
case delete_authenticator(?CHAIN, ID) of
|
||||
|
@ -162,12 +162,9 @@ post_config_update({update_authenticator, ID, #{<<"name">> := Name}}, NewConfig,
|
|||
N =:= Name
|
||||
end, NewConfig) of
|
||||
[Config] ->
|
||||
case update_authenticator(?CHAIN, ID, Config) of
|
||||
{ok, _} -> ok;
|
||||
{error, Reason} -> throw(Reason)
|
||||
end;
|
||||
update_authenticator(?CHAIN, ID, Config);
|
||||
[_Config | _] ->
|
||||
error(name_has_be_used)
|
||||
{error, name_has_be_used}
|
||||
end;
|
||||
post_config_update({update_or_create_authenticator, ID, #{<<"name">> := Name}}, NewConfig, _OldConfig) ->
|
||||
case lists:filter(
|
||||
|
@ -175,14 +172,11 @@ post_config_update({update_or_create_authenticator, ID, #{<<"name">> := Name}},
|
|||
N =:= Name
|
||||
end, NewConfig) of
|
||||
[Config] ->
|
||||
case update_or_create_authenticator(?CHAIN, ID, Config) of
|
||||
{ok, _} -> ok;
|
||||
{error, Reason} -> throw(Reason)
|
||||
end;
|
||||
update_or_create_authenticator(?CHAIN, ID, Config);
|
||||
[_Config | _] ->
|
||||
error(name_has_be_used)
|
||||
{error, name_has_be_used}
|
||||
end;
|
||||
post_config_update({move, ID, Position}, _NewConfig, _OldConfig) ->
|
||||
post_config_update({move_authenticator, ID, Position}, _NewConfig, _OldConfig) ->
|
||||
NPosition = case Position of
|
||||
<<"top">> -> top;
|
||||
<<"bottom">> -> bottom;
|
||||
|
@ -191,16 +185,13 @@ post_config_update({move, ID, Position}, _NewConfig, _OldConfig) ->
|
|||
[<<"before">>, ID0] ->
|
||||
{before, ID0};
|
||||
_ ->
|
||||
error({invalid_parameter, position})
|
||||
{error, {invalid_parameter, position}}
|
||||
end
|
||||
end,
|
||||
case move_authenticator(?CHAIN, ID, NPosition) of
|
||||
ok -> ok;
|
||||
{error, Reason} -> throw(Reason)
|
||||
end.
|
||||
move_authenticator(?CHAIN, ID, NPosition).
|
||||
|
||||
update_config(Path, ConfigRequest) ->
|
||||
emqx_config:update(emqx_authn_schema, Path, ConfigRequest).
|
||||
emqx:update_config(Path, ConfigRequest, #{rawconf_with_defaults => true}).
|
||||
|
||||
enable() ->
|
||||
case emqx:hook('client.authenticate', {?MODULE, authenticate, []}) of
|
||||
|
@ -522,7 +513,6 @@ do_create_authenticator(ChainID, AuthenticatorID, #{name := Name} = Config) ->
|
|||
Authenticator = #authenticator{id = AuthenticatorID,
|
||||
name = Name,
|
||||
provider = Provider,
|
||||
config = Config,
|
||||
state = switch_version(State)},
|
||||
{ok, Authenticator};
|
||||
{error, Reason} ->
|
||||
|
@ -570,8 +560,7 @@ update_or_create_authenticator(ChainID, AuthenticatorID, #{name := NewName} = Co
|
|||
case Provider:update(Config#{'_unique' => Unique}, State) of
|
||||
{ok, NewState} ->
|
||||
NewAuthenticator = Authenticator#authenticator{name = NewName,
|
||||
config = Config,
|
||||
state = switch_version(NewState)},
|
||||
state = switch_version(NewState)},
|
||||
NewAuthenticators = replace_authenticator(AuthenticatorID, NewAuthenticator, Authenticators),
|
||||
true = ets:insert(?CHAIN_TAB, Chain#chain{authenticators = NewAuthenticators}),
|
||||
{ok, serialize_authenticator(NewAuthenticator)};
|
||||
|
@ -583,9 +572,8 @@ update_or_create_authenticator(ChainID, AuthenticatorID, #{name := NewName} = Co
|
|||
case NewProvider:create(Config#{'_unique' => Unique}) of
|
||||
{ok, NewState} ->
|
||||
NewAuthenticator = Authenticator#authenticator{name = NewName,
|
||||
provider = NewProvider,
|
||||
config = Config,
|
||||
state = switch_version(NewState)},
|
||||
provider = NewProvider,
|
||||
state = switch_version(NewState)},
|
||||
NewAuthenticators = replace_authenticator(AuthenticatorID, NewAuthenticator, Authenticators),
|
||||
true = ets:insert(?CHAIN_TAB, Chain#chain{authenticators = NewAuthenticators}),
|
||||
_ = Provider:destroy(State),
|
||||
|
@ -660,5 +648,7 @@ serialize_authenticators(Authenticators) ->
|
|||
[serialize_authenticator(Authenticator) || {_, _, Authenticator} <- Authenticators].
|
||||
|
||||
serialize_authenticator(#authenticator{id = ID,
|
||||
config = Config}) ->
|
||||
Config#{id => ID}.
|
||||
name = Name,
|
||||
provider = Provider,
|
||||
state = State}) ->
|
||||
#{id => ID, name => Name, provider => Provider, state => State}.
|
||||
|
|
|
@ -790,6 +790,7 @@ definitions() ->
|
|||
, minirest:ref(<<"password_based_mysql">>)
|
||||
, minirest:ref(<<"password_based_pgsql">>)
|
||||
, minirest:ref(<<"password_based_mongodb">>)
|
||||
, minirest:ref(<<"password_based_redis">>)
|
||||
, minirest:ref(<<"password_based_http_server">>)
|
||||
]
|
||||
}
|
||||
|
@ -1292,7 +1293,7 @@ authentication(post, Request) ->
|
|||
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||
case emqx_json:decode(Body, [return_maps]) of
|
||||
#{<<"enable">> := Enable} ->
|
||||
emqx_authn:update_config([authentication, enable], {enable, Enable}),
|
||||
{ok, _} = emqx_authn:update_config([authentication, enable], {enable, Enable}),
|
||||
{204};
|
||||
_ ->
|
||||
serialize_error({missing_parameter, enable})
|
||||
|
@ -1305,20 +1306,28 @@ authenticators(post, Request) ->
|
|||
{ok, Body, _} = cowboy_req:read_body(Request),
|
||||
Config = emqx_json:decode(Body, [return_maps]),
|
||||
case emqx_authn:update_config([authentication, authenticators], {create_authenticator, Config}) of
|
||||
ok ->
|
||||
{204};
|
||||
{error, Reason} ->
|
||||
{ok, #{post_config_update := #{emqx_authn := #{id := ID, name := Name}},
|
||||
raw_config := RawConfig}} ->
|
||||
[RawConfig1] = [RC || #{<<"name">> := N} = RC <- RawConfig, N =:= Name],
|
||||
{200, RawConfig1#{id => ID}};
|
||||
{error, {_, _, Reason}} ->
|
||||
serialize_error(Reason)
|
||||
end;
|
||||
authenticators(get, _Request) ->
|
||||
RawConfig = get_raw_config([authentication, authenticators]),
|
||||
{ok, Authenticators} = emqx_authn:list_authenticators(?CHAIN),
|
||||
{200, Authenticators}.
|
||||
NAuthenticators = lists:zipwith(fun(#{<<"name">> := Name} = Config, #{id := ID, name := Name}) ->
|
||||
Config#{id => ID}
|
||||
end, RawConfig, Authenticators),
|
||||
{200, NAuthenticators}.
|
||||
|
||||
authenticators2(get, Request) ->
|
||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||
case emqx_authn:lookup_authenticator(?CHAIN, AuthenticatorID) of
|
||||
{ok, Authenticator} ->
|
||||
{200, Authenticator};
|
||||
{ok, #{id := ID, name := Name}} ->
|
||||
RawConfig = get_raw_config([authentication, authenticators]),
|
||||
[RawConfig1] = [RC || #{<<"name">> := N} = RC <- RawConfig, N =:= Name],
|
||||
{200, RawConfig1#{id => ID}};
|
||||
{error, Reason} ->
|
||||
serialize_error(Reason)
|
||||
end;
|
||||
|
@ -1328,17 +1337,19 @@ authenticators2(put, Request) ->
|
|||
Config = emqx_json:decode(Body, [return_maps]),
|
||||
case emqx_authn:update_config([authentication, authenticators],
|
||||
{update_or_create_authenticator, AuthenticatorID, Config}) of
|
||||
ok ->
|
||||
{204};
|
||||
{error, Reason} ->
|
||||
{ok, #{post_config_update := #{emqx_authn := #{id := ID, name := Name}},
|
||||
raw_config := RawConfig}} ->
|
||||
[RawConfig0] = [RC || #{<<"name">> := N} = RC <- RawConfig, N =:= Name],
|
||||
{200, RawConfig0#{id => ID}};
|
||||
{error, {_, _, Reason}} ->
|
||||
serialize_error(Reason)
|
||||
end;
|
||||
authenticators2(delete, Request) ->
|
||||
AuthenticatorID = cowboy_req:binding(id, Request),
|
||||
case emqx_authn:update_config([authentication, authenticators], {delete_authenticator, AuthenticatorID}) of
|
||||
ok ->
|
||||
{ok, _} ->
|
||||
{204};
|
||||
{error, Reason} ->
|
||||
{error, {_, _, Reason}} ->
|
||||
serialize_error(Reason)
|
||||
end.
|
||||
|
||||
|
@ -1348,8 +1359,8 @@ move(post, Request) ->
|
|||
case emqx_json:decode(Body, [return_maps]) of
|
||||
#{<<"position">> := Position} ->
|
||||
case emqx_authn:update_config([authentication, authenticators], {move_authenticator, AuthenticatorID, Position}) of
|
||||
ok -> {204};
|
||||
{error, Reason} -> serialize_error(Reason)
|
||||
{ok, _} -> {204};
|
||||
{error, {_, _, Reason}} -> serialize_error(Reason)
|
||||
end;
|
||||
_ ->
|
||||
serialize_error({missing_parameter, position})
|
||||
|
@ -1361,10 +1372,8 @@ import_users(post, Request) ->
|
|||
case emqx_json:decode(Body, [return_maps]) of
|
||||
#{<<"filename">> := Filename} ->
|
||||
case emqx_authn:import_users(?CHAIN, AuthenticatorID, Filename) of
|
||||
ok ->
|
||||
{204};
|
||||
{error, Reason} ->
|
||||
serialize_error(Reason)
|
||||
ok -> {204};
|
||||
{error, Reason} -> serialize_error(Reason)
|
||||
end;
|
||||
_ ->
|
||||
serialize_error({missing_parameter, filename})
|
||||
|
@ -1435,6 +1444,11 @@ users2(delete, Request) ->
|
|||
serialize_error(Reason)
|
||||
end.
|
||||
|
||||
get_raw_config(ConfKeyPath) ->
|
||||
%% TODO: call emqx_config:get_raw(ConfKeyPath) directly
|
||||
NConfKeyPath = [atom_to_binary(Key, utf8) || Key <- ConfKeyPath],
|
||||
emqx_map_lib:deep_get(NConfKeyPath, emqx_config:fill_defaults(emqx_config:get_raw([]))).
|
||||
|
||||
serialize_error({not_found, {authenticator, ID}}) ->
|
||||
{404, #{code => <<"NOT_FOUND">>,
|
||||
message => list_to_binary(io_lib:format("Authenticator '~s' does not exist", [ID]))}};
|
||||
|
|
|
@ -71,7 +71,7 @@ t_authenticator(_) ->
|
|||
secret => <<"abcdef">>,
|
||||
secret_base64_encoded => false,
|
||||
verify_claims => []},
|
||||
{ok, #{name := AuthenticatorName1, id := ID1, mechanism := jwt}} = ?AUTH:update_authenticator(?CHAIN, ID1, AuthenticatorConfig2),
|
||||
{ok, #{name := AuthenticatorName1, id := ID1}} = ?AUTH:update_authenticator(?CHAIN, ID1, AuthenticatorConfig2),
|
||||
|
||||
ID2 = <<"random">>,
|
||||
?assertEqual({error, {not_found, {authenticator, ID2}}}, ?AUTH:update_authenticator(?CHAIN, ID2, AuthenticatorConfig2)),
|
||||
|
@ -79,9 +79,9 @@ t_authenticator(_) ->
|
|||
|
||||
AuthenticatorName2 = <<"myauthenticator2">>,
|
||||
AuthenticatorConfig3 = AuthenticatorConfig2#{name => AuthenticatorName2},
|
||||
{ok, #{name := AuthenticatorName2, id := ID2, secret := <<"abcdef">>}} = ?AUTH:update_or_create_authenticator(?CHAIN, ID2, AuthenticatorConfig3),
|
||||
{ok, #{name := AuthenticatorName2, id := ID2}} = ?AUTH:update_or_create_authenticator(?CHAIN, ID2, AuthenticatorConfig3),
|
||||
?assertMatch({ok, #{name := AuthenticatorName2}}, ?AUTH:lookup_authenticator(?CHAIN, ID2)),
|
||||
{ok, #{name := AuthenticatorName2, id := ID2, secret := <<"fedcba">>}} = ?AUTH:update_or_create_authenticator(?CHAIN, ID2, AuthenticatorConfig3#{secret := <<"fedcba">>}),
|
||||
{ok, #{name := AuthenticatorName2, id := ID2}} = ?AUTH:update_or_create_authenticator(?CHAIN, ID2, AuthenticatorConfig3#{secret := <<"fedcba">>}),
|
||||
|
||||
?assertMatch({ok, #{id := ?CHAIN, authenticators := [#{name := AuthenticatorName1}, #{name := AuthenticatorName2}]}}, ?AUTH:lookup_chain(?CHAIN)),
|
||||
?assertMatch({ok, [#{name := AuthenticatorName1}, #{name := AuthenticatorName2}]}, ?AUTH:list_authenticators(?CHAIN)),
|
||||
|
|
Loading…
Reference in New Issue