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]). |