./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 323 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 312 <<$(, UIDAttr/binary, $=, UIDAttrFormat/binary, $)>>;
81 subfilter({UIDAttr}) ->
82 %% The default UiDAttrFormat is <<"%u">>
83 11 <<$(, 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 2 find_ldap_attrs([{Attr, <<"%u">>} | Rest], Attributes);
92 find_ldap_attrs([{Attr, Format} | Rest], Attributes) ->
93 1080 case get_ldap_attr(Attr, Attributes) of
94 Value when Value /= <<>>, Value /= [] ->
95 1080 {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 1931 Res = lists:filter(
106 fun({Name, _}) ->
107 4179 case_insensitive_match(Name, LDAPAttr)
108 end, Attributes),
109 1931 case singleton_value(Res) of
110 1244 {_, Value} -> eldap_utils:maybe_list2b(Value);
111 687 _ -> <<>>
112 end.
113
114
115 -spec get_user_part(binary(), binary()) -> {ok, binary()} | {error, badmatch}.
116 get_user_part(String, Pattern) ->
117 1085 F = fun(S, P) ->
118 1085 {First, _} = binary:match(P, <<"%u">>),
119 1085 TailLength = byte_size(P) - (First+1),
120 1085 binary:part(S, First, byte_size(S)-TailLength-First+1)
121 end,
122 1085 case catch F(String, Pattern) of
123 {'EXIT', _} ->
124
:-(
{error, badmatch};
125 Result ->
126 1085 case catch re:replace(Pattern, <<"%u">>, Result, [global, {return, binary}]) of
127 {'EXIT', _} ->
128
:-(
{error, badmatch};
129 StringRes ->
130 1085 case case_insensitive_match(StringRes, String) of
131 true ->
132 1085 {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 11 Splits = binary:split(Value, <<"*">>, [global]),
144 11 {Acc, S}=case Splits of
145 1 [<<"">>|T]->{[], maybe_b2list(T)};
146 10 [H|T]-> {[{initial, maybe_b2list(H)}], T}
147 end,
148 11 lists:reverse(generate_substring_list(S, Acc)).
149 generate_substring_list([<<"">>], Acc)->
150 10 Acc;
151 generate_substring_list([Last], Acc)->
152 1 [{final, Last}|Acc];
153 generate_substring_list([H|T], Acc)->
154 1 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 19 NewUIDs = [{U, eldap_filter:do_sub(
165 19 UF, [{<<"%u">>, <<"*%u*">>, 1}])} || {U, UF} <- UIDs],
166 19 Filter = lists:flatmap(
167 traverse_filter_fun(NewUIDs), Data),
168 19 case Filter of
169 [F] ->
170 17 F;
171 _ ->
172 2 eldap:Op(Filter)
173 end.
174
175 traverse_filter_fun(NewUIDs) ->
176 19 fun(Entry) ->
177 21 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 21 case binary:match(Value, <<"*">>) of
189 10 nomatch -> [eldap:equalityMatch(Name, Value)];
190 11 _ -> [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 24709 X1 = string:to_lower(maybe_b2list(X)),
199 24709 Y1 = string:to_lower(maybe_b2list(Y)),
200 24709 case X1 == Y1 of
201 3194 true -> true;
202 21515 _-> false
203 end.
204
205
206 -spec get_state(mongooseim:host_type(), atom()) -> any().
207 get_state(HostType, Module) ->
208 10552 Proc = gen_mod:get_module_proc(HostType, Module),
209 10552 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 323 lists:map(fun({U, V}) ->
219 312 {U, eldap_filter:do_sub(V, [{<<"%d">>, Host}])};
220 11 (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 6587 case gen_mod:get_opt(Key, Opts, Default) of
228 Default ->
229 3948 Default;
230 Val ->
231 2639 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 2639 Res = case F of
238 {Mod, Fun} ->
239
:-(
catch Mod:Fun(Val);
240 _ ->
241 2639 catch F(Val)
242 end,
243 2639 case Res of
244 {'EXIT', _} ->
245
:-(
?LOG_ERROR(#{what => configuration_error, option => Opt,
246
:-(
value => Val, default => Default}),
247
:-(
Default;
248 _ ->
249 2639 Res
250 end.
251
252 get_base(Opts) ->
253 89 get_mod_opt(ldap_base, Opts, fun iolist_to_binary/1, <<"">>).
254
255 get_deref_aliases(Opts) ->
256 89 get_mod_opt(ldap_deref, Opts, fun deref_aliases/1, neverDerefAliases).
257
258 235 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 88 UIDsTemp = get_mod_opt(ldap_uids, Opts, fun(V) -> V end, [{<<"uid">>, <<"%u">>}]),
265 88 uids_domain_subst(Host, UIDsTemp).
266
267 get_user_filter(UIDs, Opts) ->
268 88 RawUserFilter = get_mod_opt(ldap_filter, Opts, fun(V) -> V end, <<>>),
269 88 process_user_filter(UIDs, RawUserFilter).
270
271 process_user_filter(UIDs, RawUserFilter) ->
272 323 SubFilter = generate_subfilter(UIDs),
273 323 case RawUserFilter of
274 <<>> ->
275
:-(
SubFilter;
276 F ->
277 323 <<"(&", SubFilter/binary, F/binary, ")">>
278 end.
279
280 get_search_filter(UserFilter) ->
281 323 eldap_filter:do_sub(UserFilter, [{<<"%u">>, <<"*">>}]).
282
283 -spec singleton_value(list()) -> {binary(), binary()} | false.
284 singleton_value([{K, [V]}]) ->
285 1267 {K, V};
286 singleton_value([{_K, _V} = I]) ->
287
:-(
I;
288 singleton_value(_) ->
289 752 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 126712 binary_to_list(B);
411 maybe_b2list(L) when is_list(L) ->
412 32711 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 1272 B;
420 maybe_list2b(O) ->
421
:-(
{error, {unknown_type, O}}.
Line Hits Source