./ct_report/coverage/eldap_utils.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : eldap_utils.erl
3 %%% Author : Mickael Remond <mremond@process-one.net>
4 %%% Purpose : ejabberd LDAP helper functions
5 %%% Created : 12 Oct 2006 by Mickael Remond <mremond@process-one.net>
6 %%%
7 %%%
8 %%% ejabberd, Copyright (C) 2002-2013 ProcessOne
9 %%%
10 %%% This program is free software; you can redistribute it and/or
11 %%% modify it under the terms of the GNU General Public License as
12 %%% published by the Free Software Foundation; either version 2 of the
13 %%% License, or (at your option) any later version.
14 %%%
15 %%% This program is distributed in the hope that it will be useful,
16 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 %%% General Public License for more details.
19 %%%
20 %%% You should have received a copy of the GNU General Public License
21 %%% along with this program; if not, write to the Free Software
22 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 %%%
24 %%%----------------------------------------------------------------------
25
26 -module(eldap_utils).
27 -author('mremond@process-one.net').
28
29 -export([generate_subfilter/1,
30 find_ldap_attrs/2,
31 get_ldap_attr/2,
32 get_user_part/2,
33 make_filter/2,
34 make_filter/3,
35 get_state/2,
36 case_insensitive_match/2,
37 get_mod_opt/4,
38 get_base/1,
39 get_deref_aliases/1,
40 deref_aliases/1,
41 get_uids/2,
42 get_user_filter/2,
43 process_user_filter/2,
44 get_search_filter/1,
45 decode_octet_string/3,
46 uids_domain_subst/2,
47 singleton_value/1,
48 maybe_list2b/1,
49 maybe_b2list/1]).
50
51 -ignore_xref([decode_octet_string/3, generate_subfilter/1, make_filter/2, uids_domain_subst/2]).
52
53 -include("mongoose.hrl").
54 -include("eldap.hrl").
55
56 -type dn() :: binary().
57 -type deref() :: neverDerefAliases | derefInSearching
58 | derefFindingBaseObj | derefAlways.
59 %% Used to access mongoose_wpool
60 -type eldap_id() :: {HostType :: mongooseim:host_type(), Tag :: mongoose_wpool:tag()}.
61
62 -export_type([dn/0,
63 deref/0,
64 eldap_id/0]).
65
66 %% @doc Generate an 'or' LDAP query on one or several attributes
67 %% If there is only one attribute
68 -spec generate_subfilter([{binary()} | {binary(), binary()}]) -> binary().
69 generate_subfilter([UID]) ->
70
:-(
subfilter(UID);
71 %% If there is several attributes
72 generate_subfilter(UIDs) ->
73
:-(
iolist_to_binary(["(|", [subfilter(UID) || UID <- UIDs], ")"]).
74
75
76 %% @doc Subfilter for a single attribute
77 -spec subfilter({binary()} | {binary(), binary()}) -> binary().
78 subfilter({UIDAttr, UIDAttrFormat}) ->
79 %% The default UiDAttrFormat is %u
80
:-(
<<$(, UIDAttr/binary, $=, UIDAttrFormat/binary, $)>>;
81 subfilter({UIDAttr}) ->
82 %% The default UiDAttrFormat is <<"%u">>
83
:-(
<<$(, UIDAttr/binary, $=, "%u)">>.
84
85
86 %% @doc Not tail-recursive, but it is not very terribly.
87 %% It stops finding on the first not empty value.
88 -spec find_ldap_attrs([{binary()} | {binary(), binary()}],
89 [{binary(), [binary()]}]) -> <<>> | {binary(), binary()}.
90 find_ldap_attrs([{Attr} | Rest], Attributes) ->
91
:-(
find_ldap_attrs([{Attr, <<"%u">>} | Rest], Attributes);
92 find_ldap_attrs([{Attr, Format} | Rest], Attributes) ->
93
:-(
case get_ldap_attr(Attr, Attributes) of
94 Value when Value /= <<>>, Value /= [] ->
95
:-(
{Value, Format};
96 _ ->
97
:-(
find_ldap_attrs(Rest, Attributes)
98 end;
99 find_ldap_attrs([], _) ->
100
:-(
<<>>.
101
102
103 -spec get_ldap_attr(binary(), [{binary(), [binary()]}]) -> binary().
104 get_ldap_attr(LDAPAttr, Attributes) ->
105
:-(
Res = lists:filter(
106 fun({Name, _}) ->
107
:-(
case_insensitive_match(Name, LDAPAttr)
108 end, Attributes),
109
:-(
case singleton_value(Res) of
110
:-(
{_, Value} -> eldap_utils:maybe_list2b(Value);
111
:-(
_ -> <<>>
112 end.
113
114
115 -spec get_user_part(binary(), binary()) -> {ok, binary()} | {error, badmatch}.
116 get_user_part(String, Pattern) ->
117
:-(
F = fun(S, P) ->
118
:-(
{First, _} = binary:match(P, <<"%u">>),
119
:-(
TailLength = byte_size(P) - (First+1),
120
:-(
binary:part(S, First, byte_size(S)-TailLength-First+1)
121 end,
122
:-(
case catch F(String, Pattern) of
123 {'EXIT', _} ->
124
:-(
{error, badmatch};
125 Result ->
126
:-(
case catch re:replace(Pattern, <<"%u">>, Result, [global, {return, binary}]) of
127 {'EXIT', _} ->
128
:-(
{error, badmatch};
129 StringRes ->
130
:-(
case case_insensitive_match(StringRes, String) of
131 true ->
132
:-(
{ok, Result};
133 false ->
134
:-(
{error, badmatch}
135 end
136 end
137 end.
138
139
140 -spec generate_substring_list(binary())
141 -> [{'any', binary()} | {'final', binary()} | {'initial', binary()}].
142 generate_substring_list(Value)->
143
:-(
Splits = binary:split(Value, <<"*">>, [global]),
144
:-(
{Acc, S}=case Splits of
145
:-(
[<<"">>|T]->{[], maybe_b2list(T)};
146
:-(
[H|T]-> {[{initial, maybe_b2list(H)}], T}
147 end,
148
:-(
lists:reverse(generate_substring_list(S, Acc)).
149 generate_substring_list([<<"">>], Acc)->
150
:-(
Acc;
151 generate_substring_list([Last], Acc)->
152
:-(
[{final, Last}|Acc];
153 generate_substring_list([H|T], Acc)->
154
:-(
generate_substring_list(T, [{any, H}|Acc]).
155
156
157 -spec make_filter([{binary(), [binary()]}], [{binary(), binary()}]) -> any().
158 make_filter(Data, UIDs) ->
159
:-(
make_filter(Data, UIDs, 'and').
160
161 -spec make_filter([{binary(), [binary()]}], [{binary(), binary()}],
162 'or' | 'and') -> any().
163 make_filter(Data, UIDs, Op) ->
164
:-(
NewUIDs = [{U, eldap_filter:do_sub(
165
:-(
UF, [{<<"%u">>, <<"*%u*">>, 1}])} || {U, UF} <- UIDs],
166
:-(
Filter = lists:flatmap(
167 traverse_filter_fun(NewUIDs), Data),
168
:-(
case Filter of
169 [F] ->
170
:-(
F;
171 _ ->
172
:-(
eldap:Op(Filter)
173 end.
174
175 traverse_filter_fun(NewUIDs) ->
176
:-(
fun(Entry) ->
177
:-(
match_filter_name(Entry, NewUIDs)
178 end.
179
180 match_filter_name({<<"%u">>, [Value | _]}, NewUIDs) when Value /= <<"">> ->
181
:-(
case eldap_filter:parse(
182 generate_subfilter(NewUIDs),
183 [{<<"%u">>, Value}]) of
184
:-(
{ok, F} -> [F];
185
:-(
_ -> []
186 end;
187 match_filter_name({Name, [Value | _]}, _NewUIDs) when Value /= <<"">> ->
188
:-(
case binary:match(Value, <<"*">>) of
189
:-(
nomatch -> [eldap:equalityMatch(Name, Value)];
190
:-(
_ -> [eldap:substrings(maybe_b2list(Name),
191 generate_substring_list(Value))]
192 end;
193 match_filter_name(_, _) ->
194
:-(
[].
195
196 -spec case_insensitive_match(binary(), binary()) -> boolean().
197 case_insensitive_match(X, Y) ->
198
:-(
X1 = string:to_lower(maybe_b2list(X)),
199
:-(
Y1 = string:to_lower(maybe_b2list(Y)),
200
:-(
case X1 == Y1 of
201
:-(
true -> true;
202
:-(
_-> false
203 end.
204
205
206 -spec get_state(mongooseim:host_type(), atom()) -> any().
207 get_state(HostType, Module) ->
208
:-(
Proc = gen_mod:get_module_proc(HostType, Module),
209
:-(
gen_server:call(Proc, get_state).
210
211
212 %% @doc From the list of uids attribute: we look from alias domain (%d) and make
213 %% the substitution with the actual host domain. This helps when you need to
214 %% configure many virtual domains.
215 -spec uids_domain_subst(binary(), [{binary(), binary()}]) ->
216 [{binary(), binary()}].
217 uids_domain_subst(Host, UIDs) ->
218
:-(
lists:map(fun({U, V}) ->
219
:-(
{U, eldap_filter:do_sub(V, [{<<"%d">>, Host}])};
220
:-(
(A) -> A
221 end,
222 UIDs).
223
224
225 -spec get_mod_opt(atom(), list(), fun(), any()) -> any().
226 get_mod_opt(Key, Opts, F, Default) ->
227
:-(
case gen_mod:get_opt(Key, Opts, Default) of
228 Default ->
229
:-(
Default;
230 Val ->
231
:-(
prepare_opt_val(Key, Val, F, Default)
232 end.
233
234 -type check_fun() :: fun((any()) -> any()) | {module(), atom()}.
235 -spec prepare_opt_val(any(), any(), check_fun(), any()) -> any().
236 prepare_opt_val(Opt, Val, F, Default) ->
237
:-(
Res = case F of
238 {Mod, Fun} ->
239
:-(
catch Mod:Fun(Val);
240 _ ->
241
:-(
catch F(Val)
242 end,
243
:-(
case Res of
244 {'EXIT', _} ->
245
:-(
?LOG_ERROR(#{what => configuration_error, option => Opt,
246
:-(
value => Val, default => Default}),
247
:-(
Default;
248 _ ->
249
:-(
Res
250 end.
251
252 get_base(Opts) ->
253
:-(
get_mod_opt(ldap_base, Opts, fun iolist_to_binary/1, <<"">>).
254
255 get_deref_aliases(Opts) ->
256
:-(
get_mod_opt(ldap_deref, Opts, fun deref_aliases/1, neverDerefAliases).
257
258
:-(
deref_aliases(never) -> neverDerefAliases;
259
:-(
deref_aliases(searching) -> derefInSearching;
260
:-(
deref_aliases(finding) -> derefFindingBaseObj;
261
:-(
deref_aliases(always) -> derefAlways.
262
263 get_uids(Host, Opts) ->
264
:-(
UIDsTemp = get_mod_opt(ldap_uids, Opts, fun(V) -> V end, [{<<"uid">>, <<"%u">>}]),
265
:-(
uids_domain_subst(Host, UIDsTemp).
266
267 get_user_filter(UIDs, Opts) ->
268
:-(
RawUserFilter = get_mod_opt(ldap_filter, Opts, fun(V) -> V end, <<>>),
269
:-(
process_user_filter(UIDs, RawUserFilter).
270
271 process_user_filter(UIDs, RawUserFilter) ->
272
:-(
SubFilter = generate_subfilter(UIDs),
273
:-(
case RawUserFilter of
274 <<>> ->
275
:-(
SubFilter;
276 F ->
277
:-(
<<"(&", SubFilter/binary, F/binary, ")">>
278 end.
279
280 get_search_filter(UserFilter) ->
281
:-(
eldap_filter:do_sub(UserFilter, [{<<"%u">>, <<"*">>}]).
282
283 -spec singleton_value(list()) -> {binary(), binary()} | false.
284 singleton_value([{K, [V]}]) ->
285
:-(
{K, V};
286 singleton_value([{_K, _V} = I]) ->
287
:-(
I;
288 singleton_value(_) ->
289
:-(
false.
290 %%----------------------------------------
291 %% Borrowed from asn1rt_ber_bin_v2.erl
292 %%----------------------------------------
293
294 %%% The tag-number for universal types
295 -define(N_BOOLEAN, 1).
296 -define(N_INTEGER, 2).
297 -define(N_BIT_STRING, 3).
298 -define(N_OCTET_STRING, 4).
299 -define(N_NULL, 5).
300 -define(N_OBJECT_IDENTIFIER, 6).
301 -define(N_OBJECT_DESCRIPTOR, 7).
302 -define(N_EXTERNAL, 8).
303 -define(N_REAL, 9).
304 -define(N_ENUMERATED, 10).
305 -define(N_EMBEDDED_PDV, 11).
306 -define(N_SEQUENCE, 16).
307 -define(N_SET, 17).
308 -define(N_NumericString, 18).
309 -define(N_PrintableString, 19).
310 -define(N_TeletexString, 20).
311 -define(N_VideotexString, 21).
312 -define(N_IA5String, 22).
313 -define(N_UTCTime, 23).
314 -define(N_GeneralizedTime, 24).
315 -define(N_GraphicString, 25).
316 -define(N_VisibleString, 26).
317 -define(N_GeneralString, 27).
318 -define(N_UniversalString, 28).
319 -define(N_BMPString, 30).
320
321
322 -spec decode_octet_string(_, _, list()) -> binary().
323 decode_octet_string(Buffer, Range, Tags) ->
324 % NewTags = new_tags(HasTag, #tag{class=?UNIVERSAL, number=?N_OCTET_STRING}),
325
:-(
decode_restricted_string(Buffer, Range, Tags).
326
327
328 -spec decode_restricted_string(_, _, list()) -> binary().
329 decode_restricted_string(Tlv, Range, TagsIn) ->
330
:-(
Val = match_tags(Tlv, TagsIn),
331
:-(
Val2 =
332 case Val of
333 PartList = [_H|_T] -> % constructed val
334
:-(
collect_parts(PartList);
335 Bin ->
336
:-(
Bin
337 end,
338
:-(
check_and_convert_restricted_string(Val2, Range).
339
340
341 -spec check_and_convert_restricted_string(iolist(), _) -> binary().
342 check_and_convert_restricted_string(Val, Range) ->
343
:-(
{StrLen, NewVal} = if is_binary(Val) ->
344
:-(
{size(Val), Val};
345 true ->
346
:-(
{length(Val), list_to_binary(Val)}
347 end,
348
:-(
case Range of
349 [] -> % No length constraint
350
:-(
NewVal;
351 {Lb, Ub} when StrLen >= Lb, Ub >= StrLen -> % variable length constraint
352
:-(
NewVal;
353 {{Lb, _Ub}, []} when StrLen >= Lb ->
354
:-(
NewVal;
355 {{Lb, _Ub}, _Ext=[Min|_]} when StrLen >= Lb; StrLen >= Min ->
356
:-(
NewVal;
357 {{Lb1, Ub1}, {Lb2, Ub2}} when StrLen >= Lb1, StrLen =< Ub1;
358 StrLen =< Ub2, StrLen >= Lb2 ->
359
:-(
NewVal;
360 StrLen -> % fixed length constraint
361
:-(
NewVal;
362 {_, _} ->
363
:-(
exit({error, {asn1, {length, Range, Val}}});
364 _Len when is_integer(_Len) ->
365
:-(
exit({error, {asn1, {length, Range, Val}}});
366 _ -> % some strange constraint that we don't support yet
367
:-(
NewVal
368 end.
369
370 %%----------------------------------------
371 %% Decode the in buffer to bits
372 %%----------------------------------------
373 match_tags({T, V}, [T]) ->
374
:-(
V;
375 match_tags({T, V}, [T|Tt]) ->
376
:-(
match_tags(V, Tt);
377 match_tags([{T, V}], [T|Tt]) ->
378
:-(
match_tags(V, Tt);
379 match_tags(Vlist = [{T, _V}|_], [T]) ->
380
:-(
Vlist;
381 match_tags(Tlv, []) ->
382
:-(
Tlv;
383 match_tags({Tag, _V}, [T|_Tt]) ->
384
:-(
{error, {asn1, {wrong_tag, {Tag, T}}}}.
385
386
387 -spec collect_parts([{_, _}]) -> binary().
388 collect_parts(TlvList) ->
389
:-(
collect_parts(TlvList, []).
390
391
392 -spec collect_parts([{_, _}], [any()]) -> binary().
393 collect_parts([{_, L}|Rest], Acc) when is_list(L) ->
394
:-(
collect_parts(Rest, [collect_parts(L)|Acc]);
395 collect_parts([{?N_BIT_STRING, <<Unused, Bits/binary>>}|Rest], _Acc) ->
396
:-(
collect_parts_bit(Rest, [Bits], Unused);
397 collect_parts([{_T, V}|Rest], Acc) ->
398
:-(
collect_parts(Rest, [V|Acc]);
399 collect_parts([], Acc) ->
400
:-(
list_to_binary(lists:reverse(Acc)).
401
402
403 -spec collect_parts_bit([{3, binary()}], [binary(), ...], non_neg_integer()) -> binary().
404 collect_parts_bit([{?N_BIT_STRING, <<Unused, Bits/binary>>}|Rest], Acc, Uacc) ->
405
:-(
collect_parts_bit(Rest, [Bits|Acc], Unused+Uacc);
406 collect_parts_bit([], Acc, Uacc) ->
407
:-(
maybe_list2b([Uacc|lists:reverse(Acc)]).
408
409 maybe_b2list(B) when is_binary(B) ->
410
:-(
binary_to_list(B);
411 maybe_b2list(L) when is_list(L) ->
412
:-(
L;
413 maybe_b2list(O) ->
414
:-(
{error, {unknown_type, O}}.
415
416 maybe_list2b(L) when is_list(L) ->
417
:-(
list_to_binary(L);
418 maybe_list2b(B) when is_binary(B) ->
419
:-(
B;
420 maybe_list2b(O) ->
421
:-(
{error, {unknown_type, O}}.
Line Hits Source