refactor(ldap): merge the `ldap-bind` backend as a type for the `ldap` backend

This commit is contained in:
firest 2023-10-25 18:07:00 +08:00
parent 9dfffd90d0
commit cbfd02d1b0
14 changed files with 334 additions and 296 deletions

View File

@ -26,10 +26,6 @@
-define(AUTHN_BACKEND, ldap).
-define(AUTHN_BACKEND_BIN, <<"ldap">>).
-define(AUTHN_BACKEND_BIND, ldap_bind).
-define(AUTHN_BACKEND_BIND_BIN, <<"ldap_bind">>).
-define(AUTHN_TYPE, {?AUTHN_MECHANISM, ?AUTHN_BACKEND}).
-define(AUTHN_TYPE_BIND, {?AUTHN_MECHANISM, ?AUTHN_BACKEND_BIND}).
-endif.

View File

@ -25,12 +25,10 @@
start(_StartType, _StartArgs) ->
ok = emqx_authz:register_source(?AUTHZ_TYPE, emqx_authz_ldap),
ok = emqx_authn:register_provider(?AUTHN_TYPE, emqx_authn_ldap),
ok = emqx_authn:register_provider(?AUTHN_TYPE_BIND, emqx_authn_ldap_bind),
{ok, Sup} = emqx_auth_ldap_sup:start_link(),
{ok, Sup}.
stop(_State) ->
ok = emqx_authn:deregister_provider(?AUTHN_TYPE),
ok = emqx_authn:deregister_provider(?AUTHN_TYPE_BIND),
ok = emqx_authz:unregister_source(?AUTHZ_TYPE),
ok.

View File

@ -16,19 +16,10 @@
-module(emqx_authn_ldap).
-include_lib("emqx_auth/include/emqx_authn.hrl").
-include_lib("emqx/include/logger.hrl").
-include_lib("eldap/include/eldap.hrl").
-behaviour(emqx_authn_provider).
%% a compatible attribute for version 4.x
-define(ISENABLED_ATTR, "isEnabled").
-define(VALID_ALGORITHMS, [md5, ssha, sha, sha256, sha384, sha512]).
%% TODO
%% 1. Supports more salt algorithms, SMD5 SSHA 256/384/512
%% 2. Supports https://datatracker.ietf.org/doc/html/rfc3112
-export([
create/2,
update/2,
@ -69,163 +60,25 @@ authenticate(#{auth_method := _}, _) ->
ignore;
authenticate(#{password := undefined}, _) ->
{error, bad_username_or_password};
authenticate(
#{password := Password} = Credential,
#{
password_attribute := PasswordAttr,
is_superuser_attribute := IsSuperuserAttr,
query_timeout := Timeout,
resource_id := ResourceId
} = State
) ->
case
emqx_resource:simple_sync_query(
ResourceId,
{query, Credential, [PasswordAttr, IsSuperuserAttr, ?ISENABLED_ATTR], Timeout}
)
of
{ok, []} ->
ignore;
{ok, [Entry]} ->
is_enabled(Password, Entry, State);
{error, Reason} ->
?TRACE_AUTHN_PROVIDER(error, "ldap_query_failed", #{
resource => ResourceId,
timeout => Timeout,
reason => Reason
}),
ignore
authenticate(Credential, #{method := #{type := Type}} = State) ->
case Type of
hash ->
emqx_authn_ldap_hash:authenticate(Credential, State);
bind ->
emqx_authn_ldap_bind:authenticate(Credential, State)
end.
%% it used the deprecated config form
parse_config(
#{password_attribute := PasswordAttr, is_superuser_attribute := IsSuperuserAttr} = Config0
) ->
Config = maps:without([password_attribute, is_superuser_attribute], Config0),
parse_config(Config#{
method => #{
type => hash,
password_attribute => PasswordAttr,
is_superuser_attribute => IsSuperuserAttr
}
});
parse_config(Config) ->
maps:with([query_timeout, password_attribute, is_superuser_attribute], Config).
%% To compatible v4.x
is_enabled(Password, #eldap_entry{attributes = Attributes} = Entry, State) ->
IsEnabled = get_lower_bin_value(?ISENABLED_ATTR, Attributes, "true"),
case emqx_authn_utils:to_bool(IsEnabled) of
true ->
ensure_password(Password, Entry, State);
_ ->
{error, user_disabled}
end.
ensure_password(
Password,
#eldap_entry{attributes = Attributes} = Entry,
#{password_attribute := PasswordAttr} = State
) ->
case get_value(PasswordAttr, Attributes) of
undefined ->
{error, no_password};
[LDAPPassword | _] ->
extract_hash_algorithm(LDAPPassword, Password, fun try_decode_password/4, Entry, State)
end.
%% RFC 2307 format password
%% https://datatracker.ietf.org/doc/html/rfc2307
extract_hash_algorithm(LDAPPassword, Password, OnFail, Entry, State) ->
case
re:run(
LDAPPassword,
"{([^{}]+)}(.+)",
[{capture, all_but_first, list}, global]
)
of
{match, [[HashTypeStr, PasswordHashStr]]} ->
case emqx_utils:safe_to_existing_atom(string:to_lower(HashTypeStr)) of
{ok, HashType} ->
PasswordHash = to_binary(PasswordHashStr),
is_valid_algorithm(HashType, PasswordHash, Password, Entry, State);
_Error ->
{error, invalid_hash_type}
end;
_ ->
OnFail(LDAPPassword, Password, Entry, State)
end.
is_valid_algorithm(HashType, PasswordHash, Password, Entry, State) ->
case lists:member(HashType, ?VALID_ALGORITHMS) of
true ->
verify_password(HashType, PasswordHash, Password, Entry, State);
_ ->
{error, {invalid_hash_type, HashType}}
end.
%% this password is in LDIF format which is base64 encoding
try_decode_password(LDAPPassword, Password, Entry, State) ->
case safe_base64_decode(LDAPPassword) of
{ok, Decode} ->
extract_hash_algorithm(
Decode,
Password,
fun(_, _, _, _) ->
{error, invalid_password}
end,
Entry,
State
);
{error, Reason} ->
{error, {invalid_password, Reason}}
end.
%% sha with salt
%% https://www.openldap.org/faq/data/cache/347.html
verify_password(ssha, PasswordData, Password, Entry, State) ->
case safe_base64_decode(PasswordData) of
{ok, <<PasswordHash:20/binary, Salt/binary>>} ->
verify_password(sha, hash, PasswordHash, Salt, suffix, Password, Entry, State);
{ok, _} ->
{error, invalid_ssha_password};
{error, Reason} ->
{error, {invalid_password, Reason}}
end;
verify_password(
Algorithm,
Base64HashData,
Password,
Entry,
State
) ->
verify_password(Algorithm, base64, Base64HashData, <<>>, disable, Password, Entry, State).
verify_password(Algorithm, LDAPPasswordType, LDAPPassword, Salt, Position, Password, Entry, State) ->
PasswordHash = hash_password(Algorithm, Salt, Position, Password),
case compare_password(LDAPPasswordType, LDAPPassword, PasswordHash) of
true ->
{ok, is_superuser(Entry, State)};
_ ->
{error, bad_username_or_password}
end.
is_superuser(Entry, #{is_superuser_attribute := Attr} = _State) ->
Value = get_lower_bin_value(Attr, Entry#eldap_entry.attributes, "false"),
#{is_superuser => emqx_authn_utils:to_bool(Value)}.
safe_base64_decode(Data) ->
try
{ok, base64:decode(Data)}
catch
_:Reason ->
{error, {invalid_base64_data, Reason}}
end.
get_lower_bin_value(Key, Proplists, Default) ->
[Value | _] = get_value(Key, Proplists, [Default]),
to_binary(string:to_lower(Value)).
to_binary(Value) ->
erlang:list_to_binary(Value).
hash_password(Algorithm, _Salt, disable, Password) ->
hash_password(Algorithm, Password);
hash_password(Algorithm, Salt, suffix, Password) ->
hash_password(Algorithm, <<Password/binary, Salt/binary>>).
hash_password(Algorithm, Data) ->
crypto:hash(Algorithm, Data).
compare_password(hash, LDAPPasswordHash, PasswordHash) ->
emqx_passwd:compare_secure(LDAPPasswordHash, PasswordHash);
compare_password(base64, Base64HashData, PasswordHash) ->
emqx_passwd:compare_secure(Base64HashData, base64:encode(PasswordHash)).
maps:with([query_timeout, method], Config).

View File

@ -20,32 +20,13 @@
-include_lib("emqx/include/logger.hrl").
-include_lib("eldap/include/eldap.hrl").
-behaviour(emqx_authn_provider).
-export([
create/2,
update/2,
authenticate/2,
destroy/1
authenticate/2
]).
%%------------------------------------------------------------------------------
%% APIs
%%------------------------------------------------------------------------------
create(_AuthenticatorID, Config) ->
emqx_authn_ldap:do_create(?MODULE, Config).
update(Config, State) ->
emqx_authn_ldap:update(Config, State).
destroy(State) ->
emqx_authn_ldap:destroy(State).
authenticate(#{auth_method := _}, _) ->
ignore;
authenticate(#{password := undefined}, _) ->
{error, bad_username_or_password};
authenticate(
#{password := _Password} = Credential,
#{

View File

@ -1,63 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_authn_ldap_bind_schema).
-include("emqx_auth_ldap.hrl").
-include_lib("hocon/include/hoconsc.hrl").
-behaviour(emqx_authn_schema).
-export([
fields/1,
desc/1,
refs/0,
select_union_member/1
]).
refs() ->
[?R_REF(ldap_bind)].
select_union_member(#{
<<"mechanism">> := ?AUTHN_MECHANISM_BIN, <<"backend">> := ?AUTHN_BACKEND_BIND_BIN
}) ->
refs();
select_union_member(#{<<"backend">> := ?AUTHN_BACKEND_BIND_BIN}) ->
throw(#{
reason => "unknown_mechanism",
expected => ?AUTHN_MECHANISM
});
select_union_member(_) ->
undefined.
fields(ldap_bind) ->
[
{mechanism, emqx_authn_schema:mechanism(?AUTHN_MECHANISM)},
{backend, emqx_authn_schema:backend(?AUTHN_BACKEND_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.

View File

@ -0,0 +1,197 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_authn_ldap_hash).
-include_lib("emqx_auth/include/emqx_authn.hrl").
-include_lib("emqx/include/logger.hrl").
-include_lib("eldap/include/eldap.hrl").
%% a compatible attribute for version 4.x
-define(ISENABLED_ATTR, "isEnabled").
-define(VALID_ALGORITHMS, [md5, ssha, sha, sha256, sha384, sha512]).
%% TODO
%% 1. Supports more salt algorithms, SMD5 SSHA 256/384/512
%% 2. Supports https://datatracker.ietf.org/doc/html/rfc3112
-export([
authenticate/2
]).
-import(proplists, [get_value/2, get_value/3]).
%%------------------------------------------------------------------------------
%% APIs
%%------------------------------------------------------------------------------
authenticate(
#{password := Password} = Credential,
#{
method := #{
password_attribute := PasswordAttr,
is_superuser_attribute := IsSuperuserAttr
},
query_timeout := Timeout,
resource_id := ResourceId
} = State
) ->
case
emqx_resource:simple_sync_query(
ResourceId,
{query, Credential, [PasswordAttr, IsSuperuserAttr, ?ISENABLED_ATTR], Timeout}
)
of
{ok, []} ->
ignore;
{ok, [Entry]} ->
is_enabled(Password, Entry, State);
{error, Reason} ->
?TRACE_AUTHN_PROVIDER(error, "ldap_query_failed", #{
resource => ResourceId,
timeout => Timeout,
reason => Reason
}),
ignore
end.
%% To compatible v4.x
is_enabled(Password, #eldap_entry{attributes = Attributes} = Entry, State) ->
IsEnabled = get_lower_bin_value(?ISENABLED_ATTR, Attributes, "true"),
case emqx_authn_utils:to_bool(IsEnabled) of
true ->
ensure_password(Password, Entry, State);
_ ->
{error, user_disabled}
end.
ensure_password(
Password,
#eldap_entry{attributes = Attributes} = Entry,
#{method := #{password_attribute := PasswordAttr}} = State
) ->
case get_value(PasswordAttr, Attributes) of
undefined ->
{error, no_password};
[LDAPPassword | _] ->
extract_hash_algorithm(LDAPPassword, Password, fun try_decode_password/4, Entry, State)
end.
%% RFC 2307 format password
%% https://datatracker.ietf.org/doc/html/rfc2307
extract_hash_algorithm(LDAPPassword, Password, OnFail, Entry, State) ->
case
re:run(
LDAPPassword,
"{([^{}]+)}(.+)",
[{capture, all_but_first, list}, global]
)
of
{match, [[HashTypeStr, PasswordHashStr]]} ->
case emqx_utils:safe_to_existing_atom(string:to_lower(HashTypeStr)) of
{ok, HashType} ->
PasswordHash = to_binary(PasswordHashStr),
is_valid_algorithm(HashType, PasswordHash, Password, Entry, State);
_Error ->
{error, invalid_hash_type}
end;
_ ->
OnFail(LDAPPassword, Password, Entry, State)
end.
is_valid_algorithm(HashType, PasswordHash, Password, Entry, State) ->
case lists:member(HashType, ?VALID_ALGORITHMS) of
true ->
verify_password(HashType, PasswordHash, Password, Entry, State);
_ ->
{error, {invalid_hash_type, HashType}}
end.
%% this password is in LDIF format which is base64 encoding
try_decode_password(LDAPPassword, Password, Entry, State) ->
case safe_base64_decode(LDAPPassword) of
{ok, Decode} ->
extract_hash_algorithm(
Decode,
Password,
fun(_, _, _, _) ->
{error, invalid_password}
end,
Entry,
State
);
{error, Reason} ->
{error, {invalid_password, Reason}}
end.
%% sha with salt
%% https://www.openldap.org/faq/data/cache/347.html
verify_password(ssha, PasswordData, Password, Entry, State) ->
case safe_base64_decode(PasswordData) of
{ok, <<PasswordHash:20/binary, Salt/binary>>} ->
verify_password(sha, hash, PasswordHash, Salt, suffix, Password, Entry, State);
{ok, _} ->
{error, invalid_ssha_password};
{error, Reason} ->
{error, {invalid_password, Reason}}
end;
verify_password(
Algorithm,
Base64HashData,
Password,
Entry,
State
) ->
verify_password(Algorithm, base64, Base64HashData, <<>>, disable, Password, Entry, State).
verify_password(Algorithm, LDAPPasswordType, LDAPPassword, Salt, Position, Password, Entry, State) ->
PasswordHash = hash_password(Algorithm, Salt, Position, Password),
case compare_password(LDAPPasswordType, LDAPPassword, PasswordHash) of
true ->
{ok, is_superuser(Entry, State)};
_ ->
{error, bad_username_or_password}
end.
is_superuser(Entry, #{method := #{is_superuser_attribute := Attr}} = _State) ->
Value = get_lower_bin_value(Attr, Entry#eldap_entry.attributes, "false"),
#{is_superuser => emqx_authn_utils:to_bool(Value)}.
safe_base64_decode(Data) ->
try
{ok, base64:decode(Data)}
catch
_:Reason ->
{error, {invalid_base64_data, Reason}}
end.
get_lower_bin_value(Key, Proplists, Default) ->
[Value | _] = get_value(Key, Proplists, [Default]),
to_binary(string:to_lower(Value)).
to_binary(Value) ->
erlang:list_to_binary(Value).
hash_password(Algorithm, _Salt, disable, Password) ->
hash_password(Algorithm, Password);
hash_password(Algorithm, Salt, suffix, Password) ->
hash_password(Algorithm, <<Password/binary, Salt/binary>>).
hash_password(Algorithm, Data) ->
crypto:hash(Algorithm, Data).
compare_password(hash, LDAPPasswordHash, PasswordHash) ->
emqx_passwd:compare_secure(LDAPPasswordHash, PasswordHash);
compare_password(base64, Base64HashData, PasswordHash) ->
emqx_passwd:compare_secure(Base64HashData, base64:encode(PasswordHash)).

View File

@ -29,7 +29,7 @@
]).
refs() ->
[?R_REF(ldap)].
[?R_REF(ldap), ?R_REF(ldap_deprecated)].
select_union_member(#{<<"mechanism">> := ?AUTHN_MECHANISM_BIN, <<"backend">> := ?AUTHN_BACKEND_BIN}) ->
refs();
@ -41,12 +41,34 @@ select_union_member(#{<<"backend">> := ?AUTHN_BACKEND_BIN}) ->
select_union_member(_) ->
undefined.
fields(ldap_deprecated) ->
common_fields() ++
[
{password_attribute, password_attribute()},
{is_superuser_attribute, is_superuser_attribute()}
];
fields(ldap) ->
common_fields() ++
[
{method,
?HOCON(
?UNION([?R_REF(hash_method), ?R_REF(bind_method)]),
#{desc => ?DESC(method)}
)}
];
fields(hash_method) ->
[
{type, method_type(hash)},
{password_attribute, password_attribute()},
{is_superuser_attribute, is_superuser_attribute()}
];
fields(bind_method) ->
[{type, method_type(bind)}] ++ emqx_ldap:fields(bind_opts).
common_fields() ->
[
{mechanism, emqx_authn_schema:mechanism(?AUTHN_MECHANISM)},
{backend, emqx_authn_schema:backend(?AUTHN_BACKEND)},
{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() ++
@ -54,18 +76,35 @@ fields(ldap) ->
desc(ldap) ->
?DESC(ldap);
desc(ldap_deprecated) ->
?DESC(ldap_deprecated);
desc(hash_method) ->
?DESC(hash_method);
desc(bind_method) ->
?DESC(bind_method);
desc(_) ->
undefined.
password_attribute(type) -> string();
password_attribute(desc) -> ?DESC(?FUNCTION_NAME);
password_attribute(default) -> <<"userPassword">>;
password_attribute(_) -> undefined.
method_type(Type) ->
?HOCON(?ENUM([Type]), #{desc => ?DESC(?FUNCTION_NAME), default => Type}).
is_superuser_attribute(type) -> string();
is_superuser_attribute(desc) -> ?DESC(?FUNCTION_NAME);
is_superuser_attribute(default) -> <<"isSuperuser">>;
is_superuser_attribute(_) -> undefined.
password_attribute() ->
?HOCON(
string(),
#{
desc => ?DESC(?FUNCTION_NAME),
default => <<"userPassword">>
}
).
is_superuser_attribute() ->
?HOCON(
string(),
#{
desc => ?DESC(?FUNCTION_NAME),
default => <<"isSuperuser">>
}
).
query_timeout(type) -> emqx_schema:timeout_duration_ms();
query_timeout(desc) -> ?DESC(?FUNCTION_NAME);

View File

@ -70,6 +70,29 @@ end_per_suite(Config) ->
%% Tests
%%------------------------------------------------------------------------------
t_create_with_deprecated_cfg(_Config) ->
AuthConfig = deprecated_raw_ldap_auth_config(),
{ok, _} = emqx:update_config(
?PATH,
{create_authenticator, ?GLOBAL, AuthConfig}
),
{ok, [#{provider := emqx_authn_ldap, state := State}]} = emqx_authn_chains:list_authenticators(
?GLOBAL
),
?assertMatch(
#{
method := #{
type := hash,
is_superuser_attribute := _,
password_attribute := "not_the_default_value"
}
},
State
),
emqx_authn_test_lib:delete_config(?ResourceID).
t_create(_Config) ->
AuthConfig = raw_ldap_auth_config(),
@ -225,6 +248,19 @@ raw_ldap_auth_config() ->
<<"pool_size">> => 8
}.
deprecated_raw_ldap_auth_config() ->
#{
<<"mechanism">> => <<"password_based">>,
<<"backend">> => <<"ldap">>,
<<"server">> => ldap_server(),
<<"is_superuser_attribute">> => <<"isSuperuser">>,
<<"password_attribute">> => <<"not_the_default_value">>,
<<"base_dn">> => <<"uid=${username},ou=testdevice,dc=emqx,dc=io">>,
<<"username">> => <<"cn=root,dc=emqx,dc=io">>,
<<"password">> => <<"public">>,
<<"pool_size">> => 8
}.
user_seeds() ->
New = fun(Username, Password, Result) ->
#{

View File

@ -27,7 +27,7 @@
-define(LDAP_RESOURCE, <<"emqx_authn_ldap_bind_SUITE">>).
-define(PATH, [authentication]).
-define(ResourceID, <<"password_based:ldap_bind">>).
-define(ResourceID, <<"password_based:ldap">>).
all() ->
emqx_common_test_helpers:all(?MODULE).
@ -78,7 +78,7 @@ t_create(_Config) ->
{create_authenticator, ?GLOBAL, AuthConfig}
),
{ok, [#{provider := emqx_authn_ldap_bind}]} = emqx_authn_chains:list_authenticators(?GLOBAL),
{ok, [#{provider := emqx_authn_ldap}]} = emqx_authn_chains:list_authenticators(?GLOBAL),
emqx_authn_test_lib:delete_config(?ResourceID).
t_create_invalid(_Config) ->
@ -146,10 +146,10 @@ t_destroy(_Config) ->
{create_authenticator, ?GLOBAL, AuthConfig}
),
{ok, [#{provider := emqx_authn_ldap_bind, state := State}]} =
{ok, [#{provider := emqx_authn_ldap, state := State}]} =
emqx_authn_chains:list_authenticators(?GLOBAL),
{ok, _} = emqx_authn_ldap_bind:authenticate(
{ok, _} = emqx_authn_ldap:authenticate(
#{
username => <<"mqttuser0001">>,
password => <<"mqttuser0001">>
@ -165,7 +165,7 @@ t_destroy(_Config) ->
% Authenticator should not be usable anymore
?assertMatch(
ignore,
emqx_authn_ldap_bind:authenticate(
emqx_authn_ldap:authenticate(
#{
username => <<"mqttuser0001">>,
password => <<"mqttuser0001">>
@ -199,7 +199,7 @@ t_update(_Config) ->
% 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}
{update_authenticator, ?GLOBAL, <<"password_based:ldap">>, CorrectConfig}
),
{ok, _} = emqx_access_control:authenticate(
@ -218,14 +218,17 @@ t_update(_Config) ->
raw_ldap_auth_config() ->
#{
<<"mechanism">> => <<"password_based">>,
<<"backend">> => <<"ldap_bind">>,
<<"backend">> => <<"ldap">>,
<<"server">> => ldap_server(),
<<"base_dn">> => <<"ou=testdevice,dc=emqx,dc=io">>,
<<"filter">> => <<"(uid=${username})">>,
<<"username">> => <<"cn=root,dc=emqx,dc=io">>,
<<"password">> => <<"public">>,
<<"pool_size">> => 8,
<<"bind_password">> => <<"${password}">>
<<"method">> => #{
<<"type">> => <<"bind">>,
<<"bind_password">> => <<"${password}">>
}
}.
user_seeds() ->

View File

@ -58,8 +58,7 @@
emqx_authn_http_schema,
emqx_authn_jwt_schema,
emqx_authn_scram_mnesia_schema,
emqx_authn_ldap_schema,
emqx_authn_ldap_bind_schema
emqx_authn_ldap_schema
]).
-define(EE_AUTHN_PROVIDER_SCHEMA_MODS, [

View File

@ -92,7 +92,7 @@ parse_config(Config0) ->
%% In this feature, the `bind_password` is fixed, so it should conceal from the swagger,
%% but the connector still needs it, hence we should add it back here
ensure_bind_password(Config) ->
Config#{bind_password => <<"${password}">>}.
Config#{method => #{type => bind, bind_password => <<"${password}">>}}.
adjust_ldap_fields(Fields) ->
lists:map(fun adjust_ldap_field/1, Fields).

View File

@ -35,7 +35,7 @@
%% ===================================================================
-spec on_start(binary(), hoconsc:config(), proplists:proplist(), map()) ->
{ok, binary(), map()} | {error, _}.
on_start(InstId, #{bind_password := _} = Config, Options, State) ->
on_start(InstId, #{method := #{bind_password := _}} = Config, Options, State) ->
PoolName = pool_name(InstId),
?SLOG(info, #{
msg => "starting_ldap_bind_worker",
@ -108,15 +108,10 @@ on_query(
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) ->
prepare_template(#{method := #{bind_password := V}}, State) ->
%% This is sensitive data
%% to reduce match cases, here we reuse the existing sensitive filter key: bind_password
do_prepare_template(T, State#{bind_password => emqx_placeholder:preproc_tmpl(V)});
do_prepare_template([], State) ->
State.
State#{bind_password => emqx_placeholder:preproc_tmpl(V)}.
pool_name(InstId) ->
<<InstId/binary, "-", ?POOL_NAME_SUFFIX>>.

View File

@ -1,11 +0,0 @@
emqx_authn_ldap_bind_schema {
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"""
}

View File

@ -3,6 +3,9 @@ emqx_authn_ldap_schema {
ldap.desc:
"""Configuration of authenticator using LDAP as authentication data source."""
ldap_deprecated.desc:
"""This is a deprecated form, you should avoid using it."""
password_attribute.desc:
"""Indicates which attribute is used to represent the user's password."""
@ -21,4 +24,16 @@ query_timeout.desc:
query_timeout.label:
"""Query Timeout"""
hash_method.desc:
"""Authenticate by comparing the hashed password which was provided by the `password attribute`."""
bind_method.desc:
"""Authenticate by the LDAP bind operation."""
method.desc:
"""Authentication method."""
method_type.desc:
"""Authentication method type."""
}