./ct_report/coverage/mongoose_ldap_worker.COVER.html

1 -module(mongoose_ldap_worker).
2
3 -behaviour(gen_server).
4
5 %% gen_server callbacks
6 -export([init/1,
7 handle_call/3,
8 handle_cast/2,
9 handle_info/2,
10 terminate/2,
11 code_change/3]).
12
13 -include("mongoose.hrl").
14
15 -type state() :: #{handle := none | eldap_utils:handle(),
16 servers := [string()],
17 tls_options := list(),
18 port := pos_integer(),
19 root_dn := binary(),
20 password := binary(),
21 connect_interval := pos_integer()}.
22 -type request() :: {function(), Args :: [any()]}.
23
24 %% gen_server callbacks
25
26 -spec init(gen_mod:module_opts()) -> {ok, state()}.
27 init(Options) ->
28
:-(
State = initial_state(Options),
29
:-(
self() ! connect,
30
:-(
{ok, State}.
31
32 -spec handle_call(request(), {pid(), any()}, state()) -> {reply, any(), state()}.
33 handle_call(Request, _From, State) ->
34
:-(
{Result, NewState} = call_eldap(Request, State),
35
:-(
{reply, Result, NewState}.
36
37 -spec handle_cast(any(), state()) -> {noreply, state()}.
38 handle_cast(Cast, State) ->
39
:-(
?UNEXPECTED_CAST(Cast),
40
:-(
{noreply, State}.
41
42 -spec handle_info(any(), state()) -> {noreply, state()}.
43 handle_info(connect, State) ->
44
:-(
{noreply, connect(State)};
45 handle_info(Info, State) ->
46
:-(
?UNEXPECTED_INFO(Info),
47
:-(
{noreply, State}.
48
49 -spec terminate(any(), state()) -> ok.
50
:-(
terminate(_Reason, #{handle := none}) -> ok;
51
:-(
terminate(_Reason, #{handle := Handle}) -> eldap:close(Handle).
52
53 -spec code_change(any(), state(), any()) -> {ok, state()}.
54 code_change(_OldVsn, State, _Extra) ->
55
:-(
{ok, State}.
56
57 %% internal functions
58
59 initial_state(Opts) ->
60
:-(
Opts#{handle => none}.
61
62 call_eldap(Request, State) ->
63
:-(
case do_call_eldap(Request, State) of
64 {error, Reason} when Reason =:= ldap_closed;
65 Reason =:= {gen_tcp_error, closed} ->
66
:-(
?LOG_INFO(#{what => ldap_request_failed, reason => Reason,
67
:-(
text => <<"LDAP request failed: connection closed, reconnecting...">>}),
68
:-(
eldap:close(maps:get(handle, State)),
69
:-(
NewState = connect(State#{handle := none}),
70
:-(
retry_call_eldap(Request, NewState);
71 Result ->
72
:-(
{Result, State}
73 end.
74
75 connect(State = #{handle := none,
76 servers := Servers,
77 port := Port,
78 root_dn := RootDN,
79 password := Password,
80 connect_interval := ConnectInterval}) ->
81
:-(
AnonAuth = RootDN =:= <<>> andalso Password =:= <<>>,
82
:-(
SSLConfig = case State of
83
:-(
#{tls := TLSOptions} -> [{sslopts, just_tls:make_ssl_opts(TLSOptions)}];
84
:-(
#{} -> []
85 end,
86
:-(
case eldap:open(Servers, [{port, Port}, {anon_auth, AnonAuth}] ++ SSLConfig) of
87 {ok, Handle} ->
88
:-(
case eldap:simple_bind(Handle, RootDN, Password) of
89 ok ->
90
:-(
?LOG_INFO(#{what => ldap_connected,
91
:-(
text => <<"Connected to LDAP server">>}),
92
:-(
State#{handle := Handle};
93 Error ->
94
:-(
?LOG_ERROR(#{what => ldap_auth_failed, reason => Error,
95 text => <<"LDAP bind returns an error">>,
96
:-(
ldap_servers => Servers, port => Port}),
97
:-(
eldap:close(Handle),
98
:-(
erlang:send_after(ConnectInterval, self(), connect),
99
:-(
State
100 end;
101 Error ->
102
:-(
?LOG_ERROR(#{what => ldap_connect_failed,
103 text => <<"LDAP open returns an error">>,
104
:-(
ldap_servers => Servers, port => Port, reason => Error}),
105
:-(
erlang:send_after(ConnectInterval, self(), connect),
106
:-(
State
107 end.
108
109 retry_call_eldap(Request, State) ->
110
:-(
Result = do_call_eldap(Request, State),
111
:-(
{Result, State}.
112
113
:-(
do_call_eldap(_Request, #{handle := none}) -> {error, not_connected};
114
:-(
do_call_eldap({F, Args}, #{handle := Handle}) -> apply(eldap, F, [Handle | Args]).
Line Hits Source