fix(sso): add convet_certs callback for sso backends

must convert certs in pre_config_update so the cert path refernces
are stored in raw config, otherwise the files might get gc:ed
This commit is contained in:
Zaiming (Stone) Shi 2023-09-27 22:32:26 +02:00
parent 45caa3bf01
commit bb49914fd6
4 changed files with 60 additions and 35 deletions

View File

@ -13,7 +13,8 @@
create/2, create/2,
update/3, update/3,
destroy/2, destroy/2,
login/3 login/3,
convert_certs/3
]). ]).
-export([types/0, modules/0, provider/1, backends/0, format/1]). -export([types/0, modules/0, provider/1, backends/0, format/1]).
@ -45,6 +46,11 @@
| {redirect, tuple()} | {redirect, tuple()}
| {error, Reason :: term()}. | {error, Reason :: term()}.
-callback convert_certs(
Dir :: file:filename_all(),
config()
) -> config().
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% Callback Interface %% Callback Interface
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
@ -68,6 +74,9 @@ destroy(Mod, State) ->
login(Mod, Req, State) -> login(Mod, Req, State) ->
Mod:login(Req, State). Mod:login(Req, State).
convert_certs(Mod, Dir, Config) ->
Mod:convert_certs(Dir, Config).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% API %% API
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------

View File

@ -22,7 +22,8 @@
login/2, login/2,
create/1, create/1,
update/2, update/2,
destroy/1 destroy/1,
convert_certs/2
]). ]).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
@ -163,3 +164,21 @@ ensure_user_exists(Username) ->
Error Error
end end
end. end.
convert_certs(Dir, Conf) ->
case
emqx_tls_lib:ensure_ssl_files(
Dir, maps:get(<<"ssl">>, Conf, undefined)
)
of
{ok, SSL} ->
new_ssl_source(Conf, SSL);
{error, Reason} ->
?SLOG(error, Reason#{msg => "bad_ssl_config"}),
throw({bad_ssl_config, Reason})
end.
new_ssl_source(Source, undefined) ->
Source;
new_ssl_source(Source, SSL) ->
Source#{<<"ssl">> => SSL}.

View File

@ -52,7 +52,7 @@
-record(?MOD_TAB, { -record(?MOD_TAB, {
backend :: atom(), backend :: atom(),
state :: map(), state :: undefined | map(),
last_error = ?NO_ERROR :: term() last_error = ?NO_ERROR :: term()
}). }).
@ -217,7 +217,7 @@ update_config(Backend, UpdateReq) ->
end. end.
pre_config_update(_, {update, _Backend, Config}, _OldConf) -> pre_config_update(_, {update, _Backend, Config}, _OldConf) ->
maybe_write_certs(Config); {ok, maybe_write_certs(Config)};
pre_config_update(_, {delete, _Backend}, undefined) -> pre_config_update(_, {delete, _Backend}, undefined) ->
throw(not_exists); throw(not_exists);
pre_config_update(_, {delete, _Backend}, _OldConf) -> pre_config_update(_, {delete, _Backend}, _OldConf) ->
@ -333,26 +333,13 @@ remove_handler() ->
ok = emqx_conf:remove_handler(?MOD_KEY_PATH('?')). ok = emqx_conf:remove_handler(?MOD_KEY_PATH('?')).
maybe_write_certs(#{<<"backend">> := Backend} = Conf) -> maybe_write_certs(#{<<"backend">> := Backend} = Conf) ->
case Dir = certs_path(Backend),
emqx_tls_lib:ensure_ssl_files( Provider = provider(Backend),
ssl_file_path(Backend), maps:get(<<"ssl">>, Conf, undefined) emqx_dashboard_sso:convert_certs(Provider, Dir, Conf).
)
of
{ok, SSL} ->
{ok, new_ssl_source(Conf, SSL)};
{error, Reason} ->
?SLOG(error, Reason#{msg => "bad_ssl_config"}),
throw({bad_ssl_config, Reason})
end.
ssl_file_path(Backend) -> certs_path(Backend) ->
filename:join(["sso", Backend]). filename:join(["sso", Backend]).
new_ssl_source(Source, undefined) ->
Source;
new_ssl_source(Source, SSL) ->
Source#{<<"ssl">> => SSL}.
update_state(Backend, State) -> update_state(Backend, State) ->
Data = ensure_backend_data(Backend), Data = ensure_backend_data(Backend),
ets:insert(?MOD_TAB, Data#?MOD_TAB{state = State}). ets:insert(?MOD_TAB, Data#?MOD_TAB{state = State}).

View File

@ -22,7 +22,8 @@
-export([ -export([
create/1, create/1,
update/2, update/2,
destroy/1 destroy/1,
convert_certs/2
]). ]).
-export([login/2, callback/2]). -export([login/2, callback/2]).
@ -102,7 +103,7 @@ desc(_) ->
create(#{sp_sign_request := true} = Config) -> create(#{sp_sign_request := true} = Config) ->
try try
do_create(ensure_cert_and_key(Config)) do_create(Config)
catch catch
Kind:Error -> Kind:Error ->
Msg = failed_to_ensure_cert_and_key, Msg = failed_to_ensure_cert_and_key,
@ -150,10 +151,31 @@ callback(_Req = #{body := Body}, #{sp := SP, dashboard_addr := DashboardAddr} =
{error, iolist_to_binary(Reason)} {error, iolist_to_binary(Reason)}
end. end.
convert_certs(
Dir,
#{<<"sp_sign_request">> := true, <<"sp_public_key">> := Cert, <<"sp_private_key">> := Key} =
Conf
) ->
case
emqx_tls_lib:ensure_ssl_files(
Dir, #{enable => ture, certfile => Cert, keyfile => Key}, #{}
)
of
{ok, #{certfile := CertPath, keyfile := KeyPath}} ->
Conf#{<<"sp_public_key">> => bin(CertPath), <<"sp_private_key">> => bin(KeyPath)};
{error, Reason} ->
?SLOG(error, #{msg => "failed_to_save_sp_sign_keys", reason => Reason}),
throw("Failed to save sp signing key(s)")
end;
convert_certs(_Dir, Conf) ->
Conf.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
bin(X) -> iolist_to_binary(X).
do_create( do_create(
#{ #{
dashboard_addr := DashboardAddr, dashboard_addr := DashboardAddr,
@ -222,18 +244,6 @@ gen_redirect_response(DashboardAddr, Username) ->
%% Helpers functions %% Helpers functions
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
ensure_cert_and_key(#{sp_public_key := Cert, sp_private_key := Key} = Config) ->
case
emqx_tls_lib:ensure_ssl_files(
?DIR, #{enable => ture, certfile => Cert, keyfile => Key}, #{}
)
of
{ok, #{certfile := CertPath, keyfile := KeyPath} = _NSSL} ->
Config#{sp_public_key => CertPath, sp_private_key => KeyPath};
{error, #{which_options := KeyPath}} ->
error({missing_key, lists:flatten(KeyPath)})
end.
%% TODO: unify with emqx_dashboard_sso_manager:ensure_user_exists/1 %% TODO: unify with emqx_dashboard_sso_manager:ensure_user_exists/1
ensure_user_exists(Username) -> ensure_user_exists(Username) ->
case emqx_dashboard_admin:lookup_user(saml, Username) of case emqx_dashboard_admin:lookup_user(saml, Username) of