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