Просмотр исходного кода

feat(ldap): accept wrapped secrets as passwords

Andrew Mayorov 2 лет назад
Родитель
Сommit
06861e377f

+ 5 - 5
apps/emqx_dashboard_sso/src/emqx_dashboard_sso_api.erl

@@ -204,7 +204,7 @@ backend(get, #{bindings := #{backend := Type}}) ->
         undefined ->
             {404, #{code => ?BACKEND_NOT_FOUND, message => <<"Backend not found">>}};
         Backend ->
-            {200, to_json(Backend)}
+            {200, to_redacted_json(Backend)}
     end;
 backend(put, #{bindings := #{backend := Backend}, body := Config}) ->
     ?SLOG(info, #{
@@ -264,9 +264,9 @@ valid_config(_, _, _) ->
     {error, invalid_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) ->
-    {200, to_json(Config)};
+    {200, to_redacted_json(Config)};
 handle_backend_update_result(ok, _) ->
     204;
 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}, _) ->
     {400, #{code => ?BAD_REQUEST, message => emqx_dashboard_sso:format(["Reason: ", Reason])}}.
 
-to_json(Data) ->
+to_redacted_json(Data) ->
     emqx_utils_maps:jsonable_map(
-        Data,
+        emqx_utils:redact(Data),
         fun(K, V) ->
             {K, emqx_utils_maps:binary_string(V)}
         end

+ 15 - 3
apps/emqx_dashboard_sso/test/emqx_dashboard_sso_ldap_SUITE.erl

@@ -10,9 +10,11 @@
 -include_lib("emqx_dashboard/include/emqx_dashboard.hrl").
 -include_lib("snabbkaffe/include/snabbkaffe.hrl").
 -include_lib("eunit/include/eunit.hrl").
+-include_lib("common_test/include/ct.hrl").
 
 -define(LDAP_HOST, "ldap").
 -define(LDAP_DEFAULT_PORT, 389).
+-define(LDAP_PASSWORD, <<"public">>).
 -define(LDAP_USER, <<"viewer1">>).
 -define(LDAP_USER_PASSWORD, <<"viewer1">>).
 -define(LDAP_BASE_DN, <<"ou=dashboard,dc=emqx,dc=io">>).
@@ -128,9 +130,19 @@ t_update({init, Config}) ->
     Config;
 t_update({'end', _Config}) ->
     ok;
-t_update(_) ->
+t_update(Config) ->
     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">>]),
     ?assertMatch(#{backend := <<"ldap">>, enable := true}, decode_json(Result)),
     ?assertMatch([#{backend := <<"ldap">>, enable := true}], get_sso()),
@@ -287,7 +299,7 @@ ldap_config(Override) ->
             <<"base_dn">> => ?LDAP_BASE_DN,
             <<"filter">> => ?LDAP_FILTER_WITH_UID,
             <<"username">> => <<"cn=root,dc=emqx,dc=io">>,
-            <<"password">> => <<"public">>,
+            <<"password">> => ?LDAP_PASSWORD,
             <<"pool_size">> => 8
         },
         Override

+ 11 - 12
apps/emqx_ldap/src/emqx_ldap.erl

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