./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 -include("eldap.hrl").
15
16 -type state() :: #{handle := none | eldap:handle(),
17 servers := [string()],
18 encrypt := none | tls,
19 tls_options := list(),
20 port := pos_integer(),
21 root_dn := binary(),
22 password := binary(),
23 connect_interval := pos_integer()}.
24 -type request() :: {function(), Args :: [any()]}.
25
26 %% gen_server callbacks
27
28 -spec init(list()) -> {ok, state()}.
29 init(Options) ->
30 800 State = initial_state(Options),
31 800 self() ! connect,
32 800 {ok, State}.
33
34 -spec handle_call(request(), {pid(), any()}, state()) -> {reply, any(), state()}.
35 handle_call(Request, _From, State) ->
36 15562 {Result, NewState} = call_eldap(Request, State),
37 15562 {reply, Result, NewState}.
38
39 -spec handle_cast(any(), state()) -> {noreply, state()}.
40 handle_cast(Cast, State) ->
41
:-(
?UNEXPECTED_CAST(Cast),
42
:-(
{noreply, State}.
43
44 -spec handle_info(any(), state()) -> {noreply, state()}.
45 handle_info(connect, State) ->
46 800 {noreply, connect(State)};
47 handle_info(Info, State) ->
48
:-(
?UNEXPECTED_INFO(Info),
49
:-(
{noreply, State}.
50
51 -spec terminate(any(), state()) -> ok.
52
:-(
terminate(_Reason, #{handle := none}) -> ok;
53
:-(
terminate(_Reason, #{handle := Handle}) -> eldap:close(Handle).
54
55 -spec code_change(any(), state(), any()) -> {ok, state()}.
56 code_change(_OldVsn, State, _Extra) ->
57
:-(
{ok, State}.
58
59 %% internal functions
60
61 initial_state(Opts) ->
62 800 Servers = eldap_utils:get_mod_opt(servers, Opts,
63 fun(L) ->
64
:-(
lists:map(fun(H) when is_list(H) -> H end, L)
65 end, ["localhost"]),
66 800 Encrypt = eldap_utils:get_mod_opt(encrypt, Opts,
67 800 fun(tls) -> tls;
68
:-(
(none) -> none
69 end, none),
70 800 TLSOptions = eldap_utils:get_mod_opt(tls_options, Opts,
71 800 fun(L) when is_list(L) -> L end, []),
72 800 Port = eldap_utils:get_mod_opt(port, Opts,
73
:-(
fun(I) when is_integer(I), I>0 -> I end,
74 case Encrypt of
75 800 tls -> ?LDAPS_PORT;
76
:-(
starttls -> ?LDAP_PORT;
77
:-(
_ -> ?LDAP_PORT
78 end),
79 800 RootDN = eldap_utils:get_mod_opt(rootdn, Opts,
80 fun iolist_to_binary/1,
81 <<"">>),
82 800 Password = eldap_utils:get_mod_opt(password, Opts,
83 fun iolist_to_binary/1,
84 <<"">>),
85 800 ConnectInterval = eldap_utils:get_mod_opt(connect_interval, Opts,
86
:-(
fun(I) when is_integer(I), I>0 -> I end,
87 default_connect_interval()),
88 800 #{handle => none,
89 servers => Servers,
90 encrypt => Encrypt,
91 tls_options => TLSOptions,
92 port => Port,
93 root_dn => RootDN,
94 password => Password,
95 connect_interval => ConnectInterval}.
96
97 call_eldap(Request, State) ->
98 15562 case do_call_eldap(Request, State) of
99 {error, Reason} when Reason =:= ldap_closed;
100 Reason =:= {gen_tcp_error, closed} ->
101
:-(
?LOG_INFO(#{what => ldap_request_failed, reason => Reason,
102
:-(
text => <<"LDAP request failed: connection closed, reconnecting...">>}),
103
:-(
eldap:close(maps:get(handle, State)),
104
:-(
NewState = connect(State#{handle := none}),
105
:-(
retry_call_eldap(Request, NewState);
106 Result ->
107 15562 {Result, State}
108 end.
109
110 connect(State = #{handle := none,
111 servers := Servers,
112 encrypt := Encrypt,
113 tls_options := TLSOptions,
114 port := Port,
115 root_dn := RootDN,
116 password := Password,
117 connect_interval := ConnectInterval}) ->
118 800 AnonAuth = RootDN =:= <<>> andalso Password =:= <<>>,
119 800 SSLConfig = case Encrypt of
120 800 tls -> [{ssl, true}, {sslopts, TLSOptions}];
121
:-(
none -> [{ssl, false}]
122 end,
123 800 case eldap:open(Servers, [{port, Port}, {anon_auth, AnonAuth}] ++ SSLConfig) of
124 {ok, Handle} ->
125 800 case eldap:simple_bind(Handle, RootDN, Password) of
126 ok ->
127 800 ?LOG_INFO(#{what => ldap_connected,
128 800 text => <<"Connected to LDAP server">>}),
129 800 State#{handle := Handle};
130 Error ->
131
:-(
?LOG_ERROR(#{what => ldap_auth_failed, reason => Error,
132 text => <<"LDAP bind returns an error">>,
133
:-(
ldap_servers => Servers, port => Port}),
134
:-(
eldap:close(Handle),
135
:-(
erlang:send_after(ConnectInterval, self(), connect),
136
:-(
State
137 end;
138 Error ->
139
:-(
?LOG_ERROR(#{what => ldap_connect_failed,
140 text => <<"LDAP open returns an error">>,
141
:-(
ldap_servers => Servers, port => Port, reason => Error}),
142
:-(
erlang:send_after(ConnectInterval, self(), connect),
143
:-(
State
144 end.
145
146 retry_call_eldap(Request, State) ->
147
:-(
Result = do_call_eldap(Request, State),
148
:-(
{Result, State}.
149
150
:-(
do_call_eldap(_Request, #{handle := none}) -> {error, not_connected};
151 15562 do_call_eldap({F, Args}, #{handle := Handle}) -> apply(eldap, F, [Handle | Args]).
152
153 default_connect_interval() ->
154 800 10000.
Line Hits Source