./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_opts(State = #{port := Port,
76 root_dn := RootDN,
77 password := Password}) ->
78
:-(
AnonAuth = RootDN =:= <<>> andalso Password =:= <<>>,
79
:-(
SSLConfig = case State of
80
:-(
#{tls := TLSOptions} -> [{sslopts, just_tls:make_ssl_opts(TLSOptions)}];
81
:-(
#{} -> []
82 end,
83
:-(
[{port, Port}, {anon_auth, AnonAuth}] ++ SSLConfig.
84
85 connect(State = #{handle := none,
86 servers := Servers,
87 port := Port,
88 root_dn := RootDN,
89 password := Password,
90 connect_interval := ConnectInterval}) ->
91
:-(
case eldap:open(Servers, connect_opts(State)) of
92 {ok, Handle} ->
93
:-(
case eldap:simple_bind(Handle, RootDN, Password) of
94 ok ->
95
:-(
?LOG_INFO(#{what => ldap_connected,
96
:-(
text => <<"Connected to LDAP server">>}),
97
:-(
State#{handle := Handle};
98 Error ->
99
:-(
?LOG_ERROR(#{what => ldap_auth_failed, reason => Error,
100 text => <<"LDAP bind returns an error">>,
101
:-(
ldap_servers => Servers, port => Port}),
102
:-(
eldap:close(Handle),
103
:-(
erlang:send_after(ConnectInterval, self(), connect),
104
:-(
State
105 end;
106 Error ->
107
:-(
?LOG_ERROR(#{what => ldap_connect_failed,
108 text => <<"LDAP open returns an error">>,
109
:-(
ldap_servers => Servers, port => Port, reason => Error}),
110
:-(
erlang:send_after(ConnectInterval, self(), connect),
111
:-(
State
112 end.
113
114 retry_call_eldap(Request, State) ->
115
:-(
Result = do_call_eldap(Request, State),
116
:-(
{Result, State}.
117
118
:-(
do_call_eldap(_Request, #{handle := none}) -> {error, not_connected};
119
:-(
do_call_eldap({F, Args}, #{handle := Handle}) -> apply(eldap, F, [Handle | Args]).
Line Hits Source