Merge pull request #11608 from lafirest/feat/ldap_bind
feat(ldap): integrate authentication with LDAP bind operation
This commit is contained in:
commit
aace9215a4
|
@ -11,11 +11,12 @@
|
|||
providers() ->
|
||||
[
|
||||
{{password_based, ldap}, emqx_ldap_authn},
|
||||
{{password_based, ldap_bind}, emqx_ldap_authn_bind},
|
||||
{gcp_device, emqx_gcp_device_authn}
|
||||
].
|
||||
|
||||
resource_provider() ->
|
||||
[emqx_ldap_authn].
|
||||
[emqx_ldap_authn, emqx_ldap_authn_bind].
|
||||
|
||||
-else.
|
||||
|
||||
|
|
|
@ -76,7 +76,21 @@ fields(config) ->
|
|||
desc => ?DESC(request_timeout),
|
||||
default => <<"5s">>
|
||||
})}
|
||||
] ++ emqx_connector_schema_lib:ssl_fields().
|
||||
] ++ emqx_connector_schema_lib:ssl_fields();
|
||||
fields(bind_opts) ->
|
||||
[
|
||||
{bind_password,
|
||||
?HOCON(
|
||||
binary(),
|
||||
#{
|
||||
desc => ?DESC(bind_password),
|
||||
default => <<"${password}">>,
|
||||
example => <<"${password}">>,
|
||||
sensitive => true,
|
||||
validator => fun emqx_schema:non_empty_string/1
|
||||
}
|
||||
)}
|
||||
].
|
||||
|
||||
server() ->
|
||||
Meta = #{desc => ?DESC("server")},
|
||||
|
@ -122,7 +136,12 @@ on_start(
|
|||
|
||||
case emqx_resource_pool:start(InstId, ?MODULE, Options) of
|
||||
ok ->
|
||||
{ok, prepare_template(Config, #{pool_name => InstId})};
|
||||
emqx_ldap_bind_worker:on_start(
|
||||
InstId,
|
||||
Config,
|
||||
Options,
|
||||
prepare_template(Config, #{pool_name => InstId})
|
||||
);
|
||||
{error, Reason} ->
|
||||
?tp(
|
||||
ldap_connector_start_failed,
|
||||
|
@ -131,11 +150,12 @@ on_start(
|
|||
{error, Reason}
|
||||
end.
|
||||
|
||||
on_stop(InstId, _State) ->
|
||||
on_stop(InstId, State) ->
|
||||
?SLOG(info, #{
|
||||
msg => "stopping_ldap_connector",
|
||||
connector => InstId
|
||||
}),
|
||||
ok = emqx_ldap_bind_worker:on_stop(InstId, State),
|
||||
emqx_resource_pool:stop(InstId).
|
||||
|
||||
on_query(InstId, {query, Data}, State) ->
|
||||
|
@ -143,7 +163,9 @@ on_query(InstId, {query, Data}, State) ->
|
|||
on_query(InstId, {query, Data, Attrs}, State) ->
|
||||
on_query(InstId, {query, Data}, [{attributes, Attrs}], State);
|
||||
on_query(InstId, {query, Data, Attrs, Timeout}, State) ->
|
||||
on_query(InstId, {query, Data}, [{attributes, Attrs}, {timeout, Timeout}], State).
|
||||
on_query(InstId, {query, Data}, [{attributes, Attrs}, {timeout, Timeout}], State);
|
||||
on_query(InstId, {bind, _Data} = Req, State) ->
|
||||
emqx_ldap_bind_worker:on_query(InstId, Req, State).
|
||||
|
||||
on_get_status(_InstId, #{pool_name := PoolName} = _State) ->
|
||||
case emqx_resource_pool:health_check_workers(PoolName, fun ?MODULE:do_get_status/1) of
|
||||
|
@ -233,7 +255,7 @@ do_ldap_query(
|
|||
{error, Reason} ->
|
||||
?SLOG(
|
||||
error,
|
||||
LogMeta#{msg => "ldap_connector_do_sql_query_failed", reason => Reason}
|
||||
LogMeta#{msg => "ldap_connector_do_query_failed", reason => Reason}
|
||||
),
|
||||
{error, {unrecoverable_error, Reason}}
|
||||
end.
|
||||
|
|
|
@ -32,7 +32,8 @@
|
|||
create/2,
|
||||
update/2,
|
||||
authenticate/2,
|
||||
destroy/1
|
||||
destroy/1,
|
||||
do_create/2
|
||||
]).
|
||||
|
||||
-import(proplists, [get_value/2, get_value/3]).
|
||||
|
@ -56,7 +57,9 @@ fields(ldap) ->
|
|||
{password_attribute, fun password_attribute/1},
|
||||
{is_superuser_attribute, fun is_superuser_attribute/1},
|
||||
{query_timeout, fun query_timeout/1}
|
||||
] ++ emqx_authn_schema:common_fields() ++ emqx_ldap:fields(config).
|
||||
] ++
|
||||
emqx_authn_schema:common_fields() ++
|
||||
emqx_ldap:fields(config).
|
||||
|
||||
desc(ldap) ->
|
||||
?DESC(ldap);
|
||||
|
@ -86,10 +89,10 @@ refs() ->
|
|||
[hoconsc:ref(?MODULE, ldap)].
|
||||
|
||||
create(_AuthenticatorID, Config) ->
|
||||
create(Config).
|
||||
do_create(?MODULE, Config).
|
||||
|
||||
create(Config0) ->
|
||||
ResourceId = emqx_authn_utils:make_resource_id(?MODULE),
|
||||
do_create(Module, Config0) ->
|
||||
ResourceId = emqx_authn_utils:make_resource_id(Module),
|
||||
{Config, State} = parse_config(Config0),
|
||||
{ok, _Data} = emqx_authn_utils:create_resource(ResourceId, emqx_ldap, Config),
|
||||
{ok, State#{resource_id => ResourceId}}.
|
||||
|
@ -142,16 +145,14 @@ authenticate(
|
|||
parse_config(Config) ->
|
||||
State = lists:foldl(
|
||||
fun(Key, Acc) ->
|
||||
Value =
|
||||
case maps:get(Key, Config) of
|
||||
Bin when is_binary(Bin) ->
|
||||
erlang:binary_to_list(Bin);
|
||||
Any ->
|
||||
Any
|
||||
end,
|
||||
Acc#{Key => Value}
|
||||
case maps:find(Key, Config) of
|
||||
{ok, Value} when is_binary(Value) ->
|
||||
Acc#{Key := erlang:binary_to_list(Value)};
|
||||
_ ->
|
||||
Acc
|
||||
end
|
||||
end,
|
||||
#{},
|
||||
Config,
|
||||
[password_attribute, is_superuser_attribute, query_timeout]
|
||||
),
|
||||
{Config, State}.
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_ldap_authn_bind).
|
||||
|
||||
-include_lib("emqx_authn/include/emqx_authn.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
-include_lib("hocon/include/hoconsc.hrl").
|
||||
-include_lib("eldap/include/eldap.hrl").
|
||||
|
||||
-behaviour(hocon_schema).
|
||||
-behaviour(emqx_authentication).
|
||||
|
||||
-export([
|
||||
namespace/0,
|
||||
tags/0,
|
||||
roots/0,
|
||||
fields/1,
|
||||
desc/1
|
||||
]).
|
||||
|
||||
-export([
|
||||
refs/0,
|
||||
create/2,
|
||||
update/2,
|
||||
authenticate/2,
|
||||
destroy/1
|
||||
]).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Hocon Schema
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
namespace() -> "authn".
|
||||
|
||||
tags() ->
|
||||
[<<"Authentication">>].
|
||||
|
||||
%% used for config check when the schema module is resolved
|
||||
roots() ->
|
||||
[{?CONF_NS, hoconsc:mk(hoconsc:ref(?MODULE, ldap_bind))}].
|
||||
|
||||
fields(ldap_bind) ->
|
||||
[
|
||||
{mechanism, emqx_authn_schema:mechanism(password_based)},
|
||||
{backend, emqx_authn_schema:backend(ldap_bind)},
|
||||
{query_timeout, fun query_timeout/1}
|
||||
] ++
|
||||
emqx_authn_schema:common_fields() ++
|
||||
emqx_ldap:fields(config) ++ emqx_ldap:fields(bind_opts).
|
||||
|
||||
desc(ldap_bind) ->
|
||||
?DESC(ldap_bind);
|
||||
desc(_) ->
|
||||
undefined.
|
||||
|
||||
query_timeout(type) -> emqx_schema:timeout_duration_ms();
|
||||
query_timeout(desc) -> ?DESC(?FUNCTION_NAME);
|
||||
query_timeout(default) -> <<"5s">>;
|
||||
query_timeout(_) -> undefined.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% APIs
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
refs() ->
|
||||
[hoconsc:ref(?MODULE, ldap_bind)].
|
||||
|
||||
create(_AuthenticatorID, Config) ->
|
||||
emqx_ldap_authn:do_create(?MODULE, Config).
|
||||
|
||||
update(Config, State) ->
|
||||
emqx_ldap_authn:update(Config, State).
|
||||
|
||||
destroy(State) ->
|
||||
emqx_ldap_authn:destroy(State).
|
||||
|
||||
authenticate(#{auth_method := _}, _) ->
|
||||
ignore;
|
||||
authenticate(#{password := undefined}, _) ->
|
||||
{error, bad_username_or_password};
|
||||
authenticate(
|
||||
#{password := _Password} = Credential,
|
||||
#{
|
||||
query_timeout := Timeout,
|
||||
resource_id := ResourceId
|
||||
} = _State
|
||||
) ->
|
||||
case
|
||||
emqx_resource:simple_sync_query(
|
||||
ResourceId,
|
||||
{query, Credential, [], Timeout}
|
||||
)
|
||||
of
|
||||
{ok, []} ->
|
||||
ignore;
|
||||
{ok, [_Entry | _]} ->
|
||||
case
|
||||
emqx_resource:simple_sync_query(
|
||||
ResourceId,
|
||||
{bind, Credential}
|
||||
)
|
||||
of
|
||||
ok ->
|
||||
{ok, #{is_superuser => false}};
|
||||
{error, Reason} ->
|
||||
?TRACE_AUTHN_PROVIDER(error, "ldap_bind_failed", #{
|
||||
resource => ResourceId,
|
||||
reason => Reason
|
||||
}),
|
||||
{error, bad_username_or_password}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?TRACE_AUTHN_PROVIDER(error, "ldap_query_failed", #{
|
||||
resource => ResourceId,
|
||||
timeout => Timeout,
|
||||
reason => Reason
|
||||
}),
|
||||
ignore
|
||||
end.
|
|
@ -0,0 +1,108 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_ldap_bind_worker).
|
||||
|
||||
-include_lib("typerefl/include/types.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
||||
-include_lib("eldap/include/eldap.hrl").
|
||||
|
||||
-export([
|
||||
on_start/4,
|
||||
on_stop/2,
|
||||
on_query/3
|
||||
]).
|
||||
|
||||
%% ecpool connect & reconnect
|
||||
-export([connect/1]).
|
||||
|
||||
-define(POOL_NAME_SUFFIX, "bind_worker").
|
||||
|
||||
%% ===================================================================
|
||||
-spec on_start(binary(), hoconsc:config(), proplists:proplist(), map()) ->
|
||||
{ok, binary(), map()} | {error, _}.
|
||||
on_start(InstId, #{bind_password := _} = Config, Options, State) ->
|
||||
PoolName = pool_name(InstId),
|
||||
?SLOG(info, #{
|
||||
msg => "starting_ldap_bind_worker",
|
||||
pool => PoolName
|
||||
}),
|
||||
|
||||
ok = emqx_resource:allocate_resource(InstId, ?MODULE, PoolName),
|
||||
case emqx_resource_pool:start(PoolName, ?MODULE, Options) of
|
||||
ok ->
|
||||
{ok, prepare_template(Config, State#{bind_pool_name => PoolName})};
|
||||
{error, Reason} ->
|
||||
?tp(
|
||||
ldap_bind_worker_start_failed,
|
||||
#{error => Reason}
|
||||
),
|
||||
{error, Reason}
|
||||
end;
|
||||
on_start(_InstId, _Config, _Options, State) ->
|
||||
{ok, State}.
|
||||
|
||||
on_stop(InstId, _State) ->
|
||||
case emqx_resource:get_allocated_resources(InstId) of
|
||||
#{?MODULE := PoolName} ->
|
||||
?SLOG(info, #{
|
||||
msg => "stopping_ldap_bind_worker",
|
||||
pool => PoolName
|
||||
}),
|
||||
emqx_resource_pool:stop(PoolName);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
on_query(
|
||||
InstId,
|
||||
{bind, Data},
|
||||
#{
|
||||
base_tokens := DNTks,
|
||||
bind_password_tokens := PWTks,
|
||||
bind_pool_name := PoolName
|
||||
} = State
|
||||
) ->
|
||||
DN = emqx_placeholder:proc_tmpl(DNTks, Data),
|
||||
Password = emqx_placeholder:proc_tmpl(PWTks, Data),
|
||||
|
||||
LogMeta = #{connector => InstId, state => State},
|
||||
?TRACE("QUERY", "ldap_connector_about_to_bind", LogMeta),
|
||||
case
|
||||
ecpool:pick_and_do(
|
||||
PoolName,
|
||||
{eldap, simple_bind, [DN, Password]},
|
||||
handover
|
||||
)
|
||||
of
|
||||
ok ->
|
||||
?tp(
|
||||
ldap_connector_query_return,
|
||||
#{result => ok}
|
||||
),
|
||||
ok;
|
||||
{error, Reason} ->
|
||||
?SLOG(
|
||||
error,
|
||||
LogMeta#{msg => "ldap_bind_failed", reason => Reason}
|
||||
),
|
||||
{error, {unrecoverable_error, Reason}}
|
||||
end.
|
||||
|
||||
%% ===================================================================
|
||||
|
||||
connect(Conf) ->
|
||||
emqx_ldap:connect(Conf).
|
||||
|
||||
prepare_template(Config, State) ->
|
||||
do_prepare_template(maps:to_list(maps:with([bind_password], Config)), State).
|
||||
|
||||
do_prepare_template([{bind_password, V} | T], State) ->
|
||||
do_prepare_template(T, State#{bind_password_tokens => emqx_placeholder:preproc_tmpl(V)});
|
||||
do_prepare_template([], State) ->
|
||||
State.
|
||||
|
||||
pool_name(InstId) ->
|
||||
<<InstId/binary, "-", ?POOL_NAME_SUFFIX>>.
|
|
@ -250,9 +250,3 @@ ldap_server() ->
|
|||
|
||||
ldap_config() ->
|
||||
emqx_ldap_SUITE:ldap_config([]).
|
||||
|
||||
start_apps(Apps) ->
|
||||
lists:foreach(fun application:ensure_all_started/1, Apps).
|
||||
|
||||
stop_apps(Apps) ->
|
||||
lists:foreach(fun application:stop/1, Apps).
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%--------------------------------------------------------------------
|
||||
-module(emqx_ldap_authn_bind_SUITE).
|
||||
|
||||
-compile(nowarn_export_all).
|
||||
-compile(export_all).
|
||||
|
||||
-include_lib("emqx_authn/include/emqx_authn.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_RESOURCE, <<"emqx_authn_ldap_bind_SUITE">>).
|
||||
|
||||
-define(PATH, [authentication]).
|
||||
-define(ResourceID, <<"password_based:ldap_bind">>).
|
||||
|
||||
all() ->
|
||||
emqx_common_test_helpers:all(?MODULE).
|
||||
|
||||
init_per_testcase(_, Config) ->
|
||||
emqx_authentication:initialize_authentication(?GLOBAL, []),
|
||||
emqx_authn_test_lib:delete_authenticators(
|
||||
[authentication],
|
||||
?GLOBAL
|
||||
),
|
||||
Config.
|
||||
|
||||
init_per_suite(Config) ->
|
||||
_ = application:load(emqx_conf),
|
||||
case emqx_common_test_helpers:is_tcp_server_available(?LDAP_HOST, ?LDAP_DEFAULT_PORT) of
|
||||
true ->
|
||||
Apps = emqx_cth_suite:start([emqx, emqx_conf, emqx_authn], #{
|
||||
work_dir => ?config(priv_dir, Config)
|
||||
}),
|
||||
{ok, _} = emqx_resource:create_local(
|
||||
?LDAP_RESOURCE,
|
||||
?RESOURCE_GROUP,
|
||||
emqx_ldap,
|
||||
ldap_config(),
|
||||
#{}
|
||||
),
|
||||
[{apps, Apps} | Config];
|
||||
false ->
|
||||
{skip, no_ldap}
|
||||
end.
|
||||
|
||||
end_per_suite(Config) ->
|
||||
emqx_authn_test_lib:delete_authenticators(
|
||||
[authentication],
|
||||
?GLOBAL
|
||||
),
|
||||
ok = emqx_resource:remove_local(?LDAP_RESOURCE),
|
||||
ok = emqx_cth_suite:stop(?config(apps, Config)).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Tests
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
t_create(_Config) ->
|
||||
AuthConfig = raw_ldap_auth_config(),
|
||||
|
||||
{ok, _} = emqx:update_config(
|
||||
?PATH,
|
||||
{create_authenticator, ?GLOBAL, AuthConfig}
|
||||
),
|
||||
|
||||
{ok, [#{provider := emqx_ldap_authn_bind}]} = emqx_authentication:list_authenticators(?GLOBAL),
|
||||
emqx_authn_test_lib:delete_config(?ResourceID).
|
||||
|
||||
t_create_invalid(_Config) ->
|
||||
AuthConfig = raw_ldap_auth_config(),
|
||||
|
||||
InvalidConfigs =
|
||||
[
|
||||
AuthConfig#{<<"server">> => <<"unknownhost:3333">>},
|
||||
AuthConfig#{<<"password">> => <<"wrongpass">>}
|
||||
],
|
||||
|
||||
lists:foreach(
|
||||
fun(Config) ->
|
||||
{ok, _} = emqx:update_config(
|
||||
?PATH,
|
||||
{create_authenticator, ?GLOBAL, Config}
|
||||
),
|
||||
emqx_authn_test_lib:delete_config(?ResourceID),
|
||||
?assertEqual(
|
||||
{error, {not_found, {chain, ?GLOBAL}}},
|
||||
emqx_authentication:list_authenticators(?GLOBAL)
|
||||
)
|
||||
end,
|
||||
InvalidConfigs
|
||||
).
|
||||
|
||||
t_authenticate(_Config) ->
|
||||
ok = lists:foreach(
|
||||
fun(Sample) ->
|
||||
ct:pal("test_user_auth sample: ~p", [Sample]),
|
||||
test_user_auth(Sample)
|
||||
end,
|
||||
user_seeds()
|
||||
).
|
||||
|
||||
test_user_auth(#{
|
||||
credentials := Credentials0,
|
||||
config_params := SpecificConfigParams,
|
||||
result := Result
|
||||
}) ->
|
||||
AuthConfig = maps:merge(raw_ldap_auth_config(), SpecificConfigParams),
|
||||
|
||||
{ok, _} = emqx:update_config(
|
||||
?PATH,
|
||||
{create_authenticator, ?GLOBAL, AuthConfig}
|
||||
),
|
||||
|
||||
Credentials = Credentials0#{
|
||||
listener => 'tcp:default',
|
||||
protocol => mqtt
|
||||
},
|
||||
|
||||
?assertEqual(Result, emqx_access_control:authenticate(Credentials)),
|
||||
|
||||
emqx_authn_test_lib:delete_authenticators(
|
||||
[authentication],
|
||||
?GLOBAL
|
||||
).
|
||||
|
||||
t_destroy(_Config) ->
|
||||
AuthConfig = raw_ldap_auth_config(),
|
||||
|
||||
{ok, _} = emqx:update_config(
|
||||
?PATH,
|
||||
{create_authenticator, ?GLOBAL, AuthConfig}
|
||||
),
|
||||
|
||||
{ok, [#{provider := emqx_ldap_authn_bind, state := State}]} =
|
||||
emqx_authentication:list_authenticators(?GLOBAL),
|
||||
|
||||
{ok, _} = emqx_ldap_authn_bind:authenticate(
|
||||
#{
|
||||
username => <<"mqttuser0001">>,
|
||||
password => <<"mqttuser0001">>
|
||||
},
|
||||
State
|
||||
),
|
||||
|
||||
emqx_authn_test_lib:delete_authenticators(
|
||||
[authentication],
|
||||
?GLOBAL
|
||||
),
|
||||
|
||||
% Authenticator should not be usable anymore
|
||||
?assertMatch(
|
||||
ignore,
|
||||
emqx_ldap_authn_bind:authenticate(
|
||||
#{
|
||||
username => <<"mqttuser0001">>,
|
||||
password => <<"mqttuser0001">>
|
||||
},
|
||||
State
|
||||
)
|
||||
).
|
||||
|
||||
t_update(_Config) ->
|
||||
CorrectConfig = raw_ldap_auth_config(),
|
||||
IncorrectConfig =
|
||||
CorrectConfig#{
|
||||
<<"base_dn">> => <<"ou=testdevice,dc=emqx,dc=io">>
|
||||
},
|
||||
|
||||
{ok, _} = emqx:update_config(
|
||||
?PATH,
|
||||
{create_authenticator, ?GLOBAL, IncorrectConfig}
|
||||
),
|
||||
|
||||
{error, _} = emqx_access_control:authenticate(
|
||||
#{
|
||||
username => <<"mqttuser0001">>,
|
||||
password => <<"mqttuser0001">>,
|
||||
listener => 'tcp:default',
|
||||
protocol => mqtt
|
||||
}
|
||||
),
|
||||
|
||||
% We update with config with correct query, provider should update and work properly
|
||||
{ok, _} = emqx:update_config(
|
||||
?PATH,
|
||||
{update_authenticator, ?GLOBAL, <<"password_based:ldap_bind">>, CorrectConfig}
|
||||
),
|
||||
|
||||
{ok, _} = emqx_access_control:authenticate(
|
||||
#{
|
||||
username => <<"mqttuser0001">>,
|
||||
password => <<"mqttuser0001">>,
|
||||
listener => 'tcp:default',
|
||||
protocol => mqtt
|
||||
}
|
||||
).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% Helpers
|
||||
%%------------------------------------------------------------------------------
|
||||
|
||||
raw_ldap_auth_config() ->
|
||||
#{
|
||||
<<"mechanism">> => <<"password_based">>,
|
||||
<<"backend">> => <<"ldap_bind">>,
|
||||
<<"server">> => ldap_server(),
|
||||
<<"base_dn">> => <<"uid=${username},ou=testdevice,dc=emqx,dc=io">>,
|
||||
<<"username">> => <<"cn=root,dc=emqx,dc=io">>,
|
||||
<<"password">> => <<"public">>,
|
||||
<<"pool_size">> => 8,
|
||||
<<"bind_password">> => <<"${password}">>
|
||||
}.
|
||||
|
||||
user_seeds() ->
|
||||
New = fun(Username, Password, Result) ->
|
||||
#{
|
||||
credentials => #{
|
||||
username => Username,
|
||||
password => Password
|
||||
},
|
||||
config_params => #{},
|
||||
result => Result
|
||||
}
|
||||
end,
|
||||
Valid =
|
||||
lists:map(
|
||||
fun(Idx) ->
|
||||
Username = erlang:iolist_to_binary(io_lib:format("mqttuser000~b", [Idx])),
|
||||
New(Username, Username, {ok, #{is_superuser => false}})
|
||||
end,
|
||||
lists:seq(1, 5)
|
||||
),
|
||||
[
|
||||
%% Not exists
|
||||
New(<<"notexists">>, <<"notexists">>, {error, not_authorized}),
|
||||
%% Wrong Password
|
||||
New(<<"mqttuser0001">>, <<"wrongpassword">>, {error, bad_username_or_password})
|
||||
| Valid
|
||||
].
|
||||
|
||||
ldap_server() ->
|
||||
iolist_to_binary(io_lib:format("~s:~B", [?LDAP_HOST, ?LDAP_DEFAULT_PORT])).
|
||||
|
||||
ldap_config() ->
|
||||
emqx_ldap_SUITE:ldap_config([]).
|
|
@ -647,6 +647,9 @@ is_sensitive_key(<<"jwt">>) -> true;
|
|||
is_sensitive_key(authorization) -> true;
|
||||
is_sensitive_key("authorization") -> true;
|
||||
is_sensitive_key(<<"authorization">>) -> true;
|
||||
is_sensitive_key(bind_password) -> true;
|
||||
is_sensitive_key("bind_password") -> true;
|
||||
is_sensitive_key(<<"bind_password">>) -> true;
|
||||
is_sensitive_key(Key) -> is_authorization(Key).
|
||||
|
||||
redact(Term) ->
|
||||
|
@ -777,7 +780,8 @@ redact_test_() ->
|
|||
secret,
|
||||
secret_key,
|
||||
security_token,
|
||||
token
|
||||
token,
|
||||
bind_password
|
||||
],
|
||||
[{case_name(Type, Key), fun() -> Case(Type, Key) end} || Key <- Keys, Type <- Types].
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Integrated the LDAP bind operation as a new authenticator.
|
|
@ -29,4 +29,9 @@ request_timeout.desc:
|
|||
request_timeout.label:
|
||||
"""Request Timeout"""
|
||||
|
||||
bind_password.desc:
|
||||
"""The template for password to bind."""
|
||||
|
||||
bind_password.label:
|
||||
"""Bind Password"""
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
emqx_ldap_authn_bind {
|
||||
|
||||
ldap_bind.desc:
|
||||
"""Configuration of authenticator using the LDAP bind operation as the authentication method."""
|
||||
|
||||
query_timeout.desc:
|
||||
"""Timeout for the LDAP query."""
|
||||
|
||||
query_timeout.label:
|
||||
"""Query Timeout"""
|
||||
}
|
Loading…
Reference in New Issue