feat(ldap): accept wrapped secrets as passwords

This commit is contained in:
Andrew Mayorov 2023-11-07 23:53:10 +07:00
parent fda395014c
commit 06861e377f
No known key found for this signature in database
GPG Key ID: 2837C62ACFBFED5D
3 changed files with 31 additions and 20 deletions

View File

@ -204,7 +204,7 @@ backend(get, #{bindings := #{backend := Type}}) ->
undefined -> undefined ->
{404, #{code => ?BACKEND_NOT_FOUND, message => <<"Backend not found">>}}; {404, #{code => ?BACKEND_NOT_FOUND, message => <<"Backend not found">>}};
Backend -> Backend ->
{200, to_json(Backend)} {200, to_redacted_json(Backend)}
end; end;
backend(put, #{bindings := #{backend := Backend}, body := Config}) -> backend(put, #{bindings := #{backend := Backend}, body := Config}) ->
?SLOG(info, #{ ?SLOG(info, #{
@ -264,9 +264,9 @@ valid_config(_, _, _) ->
{error, invalid_config}. {error, invalid_config}.
handle_backend_update_result({ok, #{backend := saml} = State}, _Config) -> handle_backend_update_result({ok, #{backend := saml} = State}, _Config) ->
{200, to_json(maps:without([idp_meta, sp], State))}; {200, to_redacted_json(maps:without([idp_meta, sp], State))};
handle_backend_update_result({ok, _State}, Config) -> handle_backend_update_result({ok, _State}, Config) ->
{200, to_json(Config)}; {200, to_redacted_json(Config)};
handle_backend_update_result(ok, _) -> handle_backend_update_result(ok, _) ->
204; 204;
handle_backend_update_result({error, not_exists}, _) -> handle_backend_update_result({error, not_exists}, _) ->
@ -278,9 +278,9 @@ handle_backend_update_result({error, Reason}, _) when is_binary(Reason) ->
handle_backend_update_result({error, Reason}, _) -> handle_backend_update_result({error, Reason}, _) ->
{400, #{code => ?BAD_REQUEST, message => emqx_dashboard_sso:format(["Reason: ", Reason])}}. {400, #{code => ?BAD_REQUEST, message => emqx_dashboard_sso:format(["Reason: ", Reason])}}.
to_json(Data) -> to_redacted_json(Data) ->
emqx_utils_maps:jsonable_map( emqx_utils_maps:jsonable_map(
Data, emqx_utils:redact(Data),
fun(K, V) -> fun(K, V) ->
{K, emqx_utils_maps:binary_string(V)} {K, emqx_utils_maps:binary_string(V)}
end end

View File

@ -10,9 +10,11 @@
-include_lib("emqx_dashboard/include/emqx_dashboard.hrl"). -include_lib("emqx_dashboard/include/emqx_dashboard.hrl").
-include_lib("snabbkaffe/include/snabbkaffe.hrl"). -include_lib("snabbkaffe/include/snabbkaffe.hrl").
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
-define(LDAP_HOST, "ldap"). -define(LDAP_HOST, "ldap").
-define(LDAP_DEFAULT_PORT, 389). -define(LDAP_DEFAULT_PORT, 389).
-define(LDAP_PASSWORD, <<"public">>).
-define(LDAP_USER, <<"viewer1">>). -define(LDAP_USER, <<"viewer1">>).
-define(LDAP_USER_PASSWORD, <<"viewer1">>). -define(LDAP_USER_PASSWORD, <<"viewer1">>).
-define(LDAP_BASE_DN, <<"ou=dashboard,dc=emqx,dc=io">>). -define(LDAP_BASE_DN, <<"ou=dashboard,dc=emqx,dc=io">>).
@ -128,9 +130,19 @@ t_update({init, Config}) ->
Config; Config;
t_update({'end', _Config}) -> t_update({'end', _Config}) ->
ok; ok;
t_update(_) -> t_update(Config) ->
Path = uri(["sso", "ldap"]), Path = uri(["sso", "ldap"]),
{ok, 200, Result} = request(put, Path, ldap_config(#{<<"enable">> => <<"true">>})), %% NOTE: this time verify that supplying password through file-based secret works.
PasswordFilename = filename:join([?config(priv_dir, Config), "passfile"]),
ok = file:write_file(PasswordFilename, ?LDAP_PASSWORD),
{ok, 200, Result} = request(
put,
Path,
ldap_config(#{
<<"enable">> => <<"true">>,
<<"password">> => iolist_to_binary(["file://", PasswordFilename])
})
),
check_running([<<"ldap">>]), check_running([<<"ldap">>]),
?assertMatch(#{backend := <<"ldap">>, enable := true}, decode_json(Result)), ?assertMatch(#{backend := <<"ldap">>, enable := true}, decode_json(Result)),
?assertMatch([#{backend := <<"ldap">>, enable := true}], get_sso()), ?assertMatch([#{backend := <<"ldap">>, enable := true}], get_sso()),
@ -287,7 +299,7 @@ ldap_config(Override) ->
<<"base_dn">> => ?LDAP_BASE_DN, <<"base_dn">> => ?LDAP_BASE_DN,
<<"filter">> => ?LDAP_FILTER_WITH_UID, <<"filter">> => ?LDAP_FILTER_WITH_UID,
<<"username">> => <<"cn=root,dc=emqx,dc=io">>, <<"username">> => <<"cn=root,dc=emqx,dc=io">>,
<<"password">> => <<"public">>, <<"password">> => ?LDAP_PASSWORD,
<<"pool_size">> => 8 <<"pool_size">> => 8
}, },
Override Override

View File

@ -53,8 +53,6 @@
filter_tokens := params_tokens() filter_tokens := params_tokens()
}. }.
-define(ECS, emqx_connector_schema_lib).
%%===================================================================== %%=====================================================================
%% Hocon schema %% Hocon schema
roots() -> roots() ->
@ -63,9 +61,9 @@ roots() ->
fields(config) -> fields(config) ->
[ [
{server, server()}, {server, server()},
{pool_size, fun ?ECS:pool_size/1}, {pool_size, fun emqx_connector_schema_lib:pool_size/1},
{username, fun ensure_username/1}, {username, fun ensure_username/1},
{password, fun ?ECS:password/1}, {password, emqx_connector_schema_lib:password_field()},
{base_dn, {base_dn,
?HOCON(binary(), #{ ?HOCON(binary(), #{
desc => ?DESC(base_dn), desc => ?DESC(base_dn),
@ -124,7 +122,7 @@ server() ->
ensure_username(required) -> ensure_username(required) ->
true; true;
ensure_username(Field) -> ensure_username(Field) ->
?ECS:username(Field). emqx_connector_schema_lib:username(Field).
%% =================================================================== %% ===================================================================
callback_mode() -> always_sync. callback_mode() -> always_sync.
@ -223,7 +221,8 @@ connect(Options) ->
OpenOpts = maps:to_list(maps:with([port, sslopts], Conf)), OpenOpts = maps:to_list(maps:with([port, sslopts], Conf)),
case eldap:open([Host], [{log, fun log/3}, {timeout, RequestTimeout} | OpenOpts]) of case eldap:open([Host], [{log, fun log/3}, {timeout, RequestTimeout} | OpenOpts]) of
{ok, Handle} = Ret -> {ok, Handle} = Ret ->
case eldap:simple_bind(Handle, Username, Password) of %% TODO: teach `eldap` to accept 0-arity closures as passwords.
case eldap:simple_bind(Handle, Username, emqx_secret:unwrap(Password)) of
ok -> Ret; ok -> Ret;
Error -> Error Error -> Error
end; end;
@ -320,13 +319,13 @@ log(Level, Format, Args) ->
). ).
prepare_template(Config, State) -> prepare_template(Config, State) ->
do_prepare_template(maps:to_list(maps:with([base_dn, filter], Config)), State). maps:fold(fun prepare_template/3, State, Config).
do_prepare_template([{base_dn, V} | T], State) -> prepare_template(base_dn, V, State) ->
do_prepare_template(T, State#{base_tokens => emqx_placeholder:preproc_tmpl(V)}); State#{base_tokens => emqx_placeholder:preproc_tmpl(V)};
do_prepare_template([{filter, V} | T], State) -> prepare_template(filter, V, State) ->
do_prepare_template(T, State#{filter_tokens => emqx_placeholder:preproc_tmpl(V)}); State#{filter_tokens => emqx_placeholder:preproc_tmpl(V)};
do_prepare_template([], State) -> prepare_template(_Entry, _, State) ->
State. State.
filter_escape(Binary) when is_binary(Binary) -> filter_escape(Binary) when is_binary(Binary) ->