emqx/apps/emqx_ldap/src/emqx_ldap_bind_worker.erl

118 lines
3.6 KiB
Erlang

%%--------------------------------------------------------------------
%% 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_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(), hocon:config(), proplists:proplist(), map()) ->
{ok, binary(), map()} | {error, _}.
on_start(InstId, #{method := #{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, DN, Data},
#{
bind_password := PWTks,
bind_pool_name := PoolName
} = State
) ->
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, #{result => ok}};
{error, 'invalidCredentials'} ->
{ok, #{result => 'invalidCredentials'}};
{error, Reason} ->
?SLOG(
error,
LogMeta#{msg => "ldap_bind_failed", reason => emqx_utils:redact(Reason)}
),
{error, {unrecoverable_error, Reason}}
end.
%% ===================================================================
connect(Conf) ->
emqx_ldap:connect(Conf).
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
State#{bind_password => emqx_placeholder:preproc_tmpl(V)}.
pool_name(InstId) ->
<<InstId/binary, "-", ?POOL_NAME_SUFFIX>>.