2016-07-29 16:39:13 +02:00
|
|
|
%%%-------------------------------------------------------------------
|
2016-12-27 10:44:07 +01:00
|
|
|
%%% File : mod_vcard_ldap.erl
|
|
|
|
%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
|
2016-07-29 16:39:13 +02:00
|
|
|
%%% Created : 29 Jul 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
2016-12-27 10:44:07 +01:00
|
|
|
%%%
|
|
|
|
%%%
|
2020-01-28 13:34:02 +01:00
|
|
|
%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
|
2016-12-27 10:44:07 +01:00
|
|
|
%%%
|
|
|
|
%%% This program is free software; you can redistribute it and/or
|
|
|
|
%%% modify it under the terms of the GNU General Public License as
|
|
|
|
%%% published by the Free Software Foundation; either version 2 of the
|
|
|
|
%%% License, or (at your option) any later version.
|
|
|
|
%%%
|
|
|
|
%%% This program is distributed in the hope that it will be useful,
|
|
|
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
%%% General Public License for more details.
|
|
|
|
%%%
|
|
|
|
%%% You should have received a copy of the GNU General Public License along
|
|
|
|
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
%%%
|
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
|
2004-07-30 23:09:55 +02:00
|
|
|
-module(mod_vcard_ldap).
|
2013-03-14 10:33:02 +01:00
|
|
|
|
2006-09-14 04:54:21 +02:00
|
|
|
-behaviour(gen_server).
|
2016-07-29 16:39:13 +02:00
|
|
|
-behaviour(mod_vcard).
|
2013-03-14 10:33:02 +01:00
|
|
|
|
2016-07-29 16:39:13 +02:00
|
|
|
%% API
|
|
|
|
-export([start_link/2]).
|
|
|
|
-export([init/2, stop/1, get_vcard/2, set_vcard/4, search/4,
|
2016-11-22 16:01:08 +01:00
|
|
|
remove_user/2, import/3, search_fields/1, search_reported/1,
|
2020-01-08 10:24:51 +01:00
|
|
|
mod_opt_type/1, mod_options/1, mod_doc/0]).
|
2016-11-22 16:23:02 +01:00
|
|
|
-export([is_search_supported/1]).
|
2013-03-14 10:33:02 +01:00
|
|
|
|
2016-07-29 16:39:13 +02:00
|
|
|
%% gen_server callbacks
|
|
|
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
|
|
|
terminate/2, code_change/3]).
|
2004-07-30 23:09:55 +02:00
|
|
|
|
2013-04-08 11:12:54 +02:00
|
|
|
-include("logger.hrl").
|
|
|
|
-include("eldap.hrl").
|
2016-07-29 16:39:13 +02:00
|
|
|
-include("xmpp.hrl").
|
2017-09-24 11:42:35 +02:00
|
|
|
-include("translate.hrl").
|
2004-07-30 23:09:55 +02:00
|
|
|
|
2005-08-25 22:48:45 +02:00
|
|
|
-define(PROCNAME, ejabberd_mod_vcard_ldap).
|
2004-07-30 23:09:55 +02:00
|
|
|
|
2013-03-14 10:33:02 +01:00
|
|
|
-record(state,
|
|
|
|
{serverhost = <<"">> :: binary(),
|
2017-08-08 16:46:26 +02:00
|
|
|
myhosts = [] :: [binary()],
|
2013-03-14 10:33:02 +01:00
|
|
|
eldap_id = <<"">> :: binary(),
|
2015-11-13 19:33:23 +01:00
|
|
|
search = false :: boolean(),
|
2013-03-14 10:33:02 +01:00
|
|
|
servers = [] :: [binary()],
|
|
|
|
backups = [] :: [binary()],
|
|
|
|
port = ?LDAP_PORT :: inet:port_number(),
|
|
|
|
tls_options = [] :: list(),
|
|
|
|
dn = <<"">> :: binary(),
|
|
|
|
base = <<"">> :: binary(),
|
|
|
|
password = <<"">> :: binary(),
|
2019-06-14 11:33:26 +02:00
|
|
|
uids = [] :: [{binary(), binary()}],
|
|
|
|
vcard_map = [] :: [{binary(), [{binary(), [binary()]}]}],
|
2013-03-14 10:33:02 +01:00
|
|
|
vcard_map_attrs = [] :: [binary()],
|
|
|
|
user_filter = <<"">> :: binary(),
|
|
|
|
search_filter :: eldap:filter(),
|
|
|
|
search_fields = [] :: [{binary(), binary()}],
|
|
|
|
search_reported = [] :: [{binary(), binary()}],
|
|
|
|
search_reported_attrs = [] :: [binary()],
|
|
|
|
deref_aliases = never :: never | searching | finding | always,
|
|
|
|
matches = 0 :: non_neg_integer()}).
|
2006-09-14 04:54:21 +02:00
|
|
|
|
2016-07-29 16:39:13 +02:00
|
|
|
%%%===================================================================
|
|
|
|
%%% API
|
|
|
|
%%%===================================================================
|
|
|
|
start_link(Host, Opts) ->
|
|
|
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
|
|
|
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
|
|
|
|
|
|
|
|
init(Host, Opts) ->
|
2006-09-14 04:54:21 +02:00
|
|
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
2013-03-14 10:33:02 +01:00
|
|
|
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
|
|
|
|
transient, 1000, worker, [?MODULE]},
|
2017-02-24 10:05:47 +01:00
|
|
|
supervisor:start_child(ejabberd_backend_sup, ChildSpec).
|
2006-09-14 04:54:21 +02:00
|
|
|
|
|
|
|
stop(Host) ->
|
|
|
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
2017-02-24 10:05:47 +01:00
|
|
|
supervisor:terminate_child(ejabberd_backend_sup, Proc),
|
|
|
|
supervisor:delete_child(ejabberd_backend_sup, Proc),
|
2017-02-14 08:25:08 +01:00
|
|
|
ok.
|
2006-09-14 04:54:21 +02:00
|
|
|
|
2016-11-22 16:23:02 +01:00
|
|
|
is_search_supported(_LServer) ->
|
|
|
|
true.
|
|
|
|
|
2016-07-29 16:39:13 +02:00
|
|
|
get_vcard(LUser, LServer) ->
|
|
|
|
{ok, State} = eldap_utils:get_state(LServer, ?PROCNAME),
|
|
|
|
VCardMap = State#state.vcard_map,
|
|
|
|
case find_ldap_user(LUser, State) of
|
|
|
|
#eldap_entry{attributes = Attributes} ->
|
2016-07-31 13:17:17 +02:00
|
|
|
VCard = ldap_attributes_to_vcard(Attributes, VCardMap,
|
|
|
|
{LUser, LServer}),
|
2017-05-17 16:13:34 +02:00
|
|
|
{ok, [xmpp:encode(VCard)]};
|
2016-07-29 16:39:13 +02:00
|
|
|
_ ->
|
2017-05-17 16:13:34 +02:00
|
|
|
{ok, []}
|
2006-09-14 04:54:21 +02:00
|
|
|
end.
|
|
|
|
|
2016-07-29 16:39:13 +02:00
|
|
|
set_vcard(_LUser, _LServer, _VCard, _VCardSearch) ->
|
|
|
|
{atomic, not_implemented}.
|
|
|
|
|
|
|
|
search_fields(LServer) ->
|
|
|
|
{ok, State} = eldap_utils:get_state(LServer, ?PROCNAME),
|
|
|
|
State#state.search_fields.
|
|
|
|
|
|
|
|
search_reported(LServer) ->
|
|
|
|
{ok, State} = eldap_utils:get_state(LServer, ?PROCNAME),
|
|
|
|
State#state.search_reported.
|
|
|
|
|
|
|
|
search(LServer, Data, _AllowReturnAll, MaxMatch) ->
|
|
|
|
{ok, State} = eldap_utils:get_state(LServer, ?PROCNAME),
|
|
|
|
Base = State#state.base,
|
|
|
|
SearchFilter = State#state.search_filter,
|
|
|
|
Eldap_ID = State#state.eldap_id,
|
|
|
|
UIDs = State#state.uids,
|
|
|
|
ReportedAttrs = State#state.search_reported_attrs,
|
|
|
|
Filter = eldap:'and'([SearchFilter,
|
|
|
|
eldap_utils:make_filter(Data, UIDs)]),
|
|
|
|
case eldap_pool:search(Eldap_ID,
|
|
|
|
[{base, Base}, {filter, Filter}, {limit, MaxMatch},
|
|
|
|
{deref_aliases, State#state.deref_aliases},
|
|
|
|
{attributes, ReportedAttrs}])
|
|
|
|
of
|
|
|
|
#eldap_search_result{entries = E} ->
|
|
|
|
search_items(E, State);
|
|
|
|
_ ->
|
|
|
|
[]
|
|
|
|
end.
|
2006-09-14 04:54:21 +02:00
|
|
|
|
2016-07-29 16:39:13 +02:00
|
|
|
search_items(Entries, State) ->
|
|
|
|
LServer = State#state.serverhost,
|
|
|
|
SearchReported = State#state.search_reported,
|
|
|
|
VCardMap = State#state.vcard_map,
|
|
|
|
UIDs = State#state.uids,
|
|
|
|
Attributes = lists:map(fun (E) ->
|
|
|
|
#eldap_entry{attributes = Attrs} = E, Attrs
|
|
|
|
end,
|
|
|
|
Entries),
|
2018-03-25 09:53:46 +02:00
|
|
|
lists:filtermap(
|
2016-07-29 16:39:13 +02:00
|
|
|
fun(Attrs) ->
|
|
|
|
case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
|
|
|
|
{U, UIDAttrFormat} ->
|
|
|
|
case eldap_utils:get_user_part(U, UIDAttrFormat) of
|
|
|
|
{ok, Username} ->
|
2017-05-11 14:49:06 +02:00
|
|
|
case ejabberd_auth:user_exists(Username,
|
2016-07-29 16:39:13 +02:00
|
|
|
LServer) of
|
|
|
|
true ->
|
|
|
|
RFields = lists:map(
|
|
|
|
fun({_, VCardName}) ->
|
|
|
|
{VCardName,
|
|
|
|
map_vcard_attr(VCardName,
|
|
|
|
Attrs,
|
|
|
|
VCardMap,
|
|
|
|
{Username,
|
2018-06-14 13:00:47 +02:00
|
|
|
ejabberd_config:get_myname()})}
|
2016-07-29 16:39:13 +02:00
|
|
|
end,
|
|
|
|
SearchReported),
|
|
|
|
J = <<Username/binary, $@, LServer/binary>>,
|
2018-03-25 09:53:46 +02:00
|
|
|
{true, [{<<"jid">>, J} | RFields]};
|
2016-07-29 16:39:13 +02:00
|
|
|
_ ->
|
2018-03-25 09:53:46 +02:00
|
|
|
false
|
2016-07-29 16:39:13 +02:00
|
|
|
end;
|
|
|
|
_ ->
|
2018-03-25 09:53:46 +02:00
|
|
|
false
|
2016-07-29 16:39:13 +02:00
|
|
|
end;
|
|
|
|
<<"">> ->
|
2018-03-25 09:53:46 +02:00
|
|
|
false
|
2016-07-29 16:39:13 +02:00
|
|
|
end
|
|
|
|
end, Attributes).
|
|
|
|
|
|
|
|
remove_user(_User, _Server) ->
|
|
|
|
{atomic, not_implemented}.
|
|
|
|
|
2016-11-22 16:01:08 +01:00
|
|
|
import(_, _, _) ->
|
2017-02-18 07:36:27 +01:00
|
|
|
ok.
|
2016-07-29 16:39:13 +02:00
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% gen_server callbacks
|
|
|
|
%%%===================================================================
|
2006-09-14 04:54:21 +02:00
|
|
|
init([Host, Opts]) ->
|
2017-02-14 08:25:08 +01:00
|
|
|
process_flag(trap_exit, true),
|
2006-09-14 04:54:21 +02:00
|
|
|
State = parse_options(Host, Opts),
|
2007-01-27 17:40:37 +01:00
|
|
|
eldap_pool:start_link(State#state.eldap_id,
|
2013-03-14 10:33:02 +01:00
|
|
|
State#state.servers, State#state.backups,
|
|
|
|
State#state.port, State#state.dn,
|
|
|
|
State#state.password, State#state.tls_options),
|
2006-09-14 04:54:21 +02:00
|
|
|
{ok, State}.
|
|
|
|
|
2016-07-29 16:39:13 +02:00
|
|
|
handle_call(get_state, _From, State) ->
|
|
|
|
{reply, {ok, State}, State};
|
2019-07-12 10:55:36 +02:00
|
|
|
handle_call(Request, From, State) ->
|
|
|
|
?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]),
|
|
|
|
{noreply, State}.
|
2005-09-04 03:58:47 +02:00
|
|
|
|
2019-07-12 10:55:36 +02:00
|
|
|
handle_cast(Msg, State) ->
|
|
|
|
?WARNING_MSG("Unexpected cast: ~p", [Msg]),
|
2016-07-29 16:39:13 +02:00
|
|
|
{noreply, State}.
|
2004-07-30 23:09:55 +02:00
|
|
|
|
2019-07-12 10:55:36 +02:00
|
|
|
handle_info(Info, State) ->
|
|
|
|
?WARNING_MSG("Unexpected info: ~p", [Info]),
|
2016-07-29 16:39:13 +02:00
|
|
|
{noreply, State}.
|
2004-07-30 23:09:55 +02:00
|
|
|
|
2016-07-29 16:39:13 +02:00
|
|
|
terminate(_Reason, _State) ->
|
|
|
|
ok.
|
2006-09-14 04:54:21 +02:00
|
|
|
|
2016-07-29 16:39:13 +02:00
|
|
|
code_change(_OldVsn, State, _Extra) ->
|
|
|
|
{ok, State}.
|
2006-09-14 04:54:21 +02:00
|
|
|
|
2016-07-29 16:39:13 +02:00
|
|
|
%%%===================================================================
|
|
|
|
%%% Internal functions
|
|
|
|
%%%===================================================================
|
2006-09-14 04:54:21 +02:00
|
|
|
find_ldap_user(User, State) ->
|
|
|
|
Base = State#state.base,
|
|
|
|
RFC2254_Filter = State#state.user_filter,
|
|
|
|
Eldap_ID = State#state.eldap_id,
|
|
|
|
VCardAttrs = State#state.vcard_map_attrs,
|
2013-03-14 10:33:02 +01:00
|
|
|
case eldap_filter:parse(RFC2254_Filter,
|
|
|
|
[{<<"%u">>, User}])
|
|
|
|
of
|
|
|
|
{ok, EldapFilter} ->
|
|
|
|
case eldap_pool:search(Eldap_ID,
|
|
|
|
[{base, Base}, {filter, EldapFilter},
|
|
|
|
{deref_aliases, State#state.deref_aliases},
|
|
|
|
{attributes, VCardAttrs}])
|
|
|
|
of
|
|
|
|
#eldap_search_result{entries = [E | _]} -> E;
|
|
|
|
_ -> false
|
|
|
|
end;
|
|
|
|
_ -> false
|
2004-07-30 23:09:55 +02:00
|
|
|
end.
|
|
|
|
|
2006-09-14 04:54:21 +02:00
|
|
|
ldap_attributes_to_vcard(Attributes, VCardMap, UD) ->
|
2019-06-14 11:33:26 +02:00
|
|
|
Attrs = lists:map(
|
|
|
|
fun({VCardName, _}) ->
|
|
|
|
{VCardName, map_vcard_attr(VCardName, Attributes, VCardMap, UD)}
|
|
|
|
end, VCardMap),
|
2016-07-31 13:17:17 +02:00
|
|
|
lists:foldl(fun ldap_attribute_to_vcard/2, #vcard_temp{}, Attrs).
|
2013-03-14 10:33:02 +01:00
|
|
|
|
2016-07-31 13:17:17 +02:00
|
|
|
-spec ldap_attribute_to_vcard({binary(), binary()}, vcard_temp()) -> vcard_temp().
|
|
|
|
ldap_attribute_to_vcard({Attr, Value}, V) ->
|
|
|
|
Ts = V#vcard_temp.tel,
|
|
|
|
Es = V#vcard_temp.email,
|
|
|
|
N = case V#vcard_temp.n of
|
|
|
|
undefined -> #vcard_name{};
|
|
|
|
_ -> V#vcard_temp.n
|
|
|
|
end,
|
|
|
|
O = case V#vcard_temp.org of
|
|
|
|
undefined -> #vcard_org{};
|
|
|
|
_ -> V#vcard_temp.org
|
|
|
|
end,
|
|
|
|
A = case V#vcard_temp.adr of
|
|
|
|
[] -> #vcard_adr{};
|
|
|
|
As -> hd(As)
|
|
|
|
end,
|
2019-06-14 11:33:26 +02:00
|
|
|
case str:to_lower(Attr) of
|
2016-07-31 13:17:17 +02:00
|
|
|
<<"fn">> -> V#vcard_temp{fn = Value};
|
|
|
|
<<"nickname">> -> V#vcard_temp{nickname = Value};
|
|
|
|
<<"title">> -> V#vcard_temp{title = Value};
|
|
|
|
<<"bday">> -> V#vcard_temp{bday = Value};
|
|
|
|
<<"url">> -> V#vcard_temp{url = Value};
|
|
|
|
<<"desc">> -> V#vcard_temp{desc = Value};
|
|
|
|
<<"role">> -> V#vcard_temp{role = Value};
|
|
|
|
<<"tel">> -> V#vcard_temp{tel = [#vcard_tel{number = Value}|Ts]};
|
|
|
|
<<"email">> -> V#vcard_temp{email = [#vcard_email{userid = Value}|Es]};
|
|
|
|
<<"photo">> -> V#vcard_temp{photo = #vcard_photo{binval = Value}};
|
2016-09-13 11:30:05 +02:00
|
|
|
<<"family">> -> V#vcard_temp{n = N#vcard_name{family = Value}};
|
|
|
|
<<"given">> -> V#vcard_temp{n = N#vcard_name{given = Value}};
|
|
|
|
<<"middle">> -> V#vcard_temp{n = N#vcard_name{middle = Value}};
|
|
|
|
<<"orgname">> -> V#vcard_temp{org = O#vcard_org{name = Value}};
|
2016-07-31 13:17:17 +02:00
|
|
|
<<"orgunit">> -> V#vcard_temp{org = O#vcard_org{units = [Value]}};
|
|
|
|
<<"locality">> -> V#vcard_temp{adr = [A#vcard_adr{locality = Value}]};
|
|
|
|
<<"street">> -> V#vcard_temp{adr = [A#vcard_adr{street = Value}]};
|
|
|
|
<<"ctry">> -> V#vcard_temp{adr = [A#vcard_adr{ctry = Value}]};
|
|
|
|
<<"region">> -> V#vcard_temp{adr = [A#vcard_adr{region = Value}]};
|
|
|
|
<<"pcode">> -> V#vcard_temp{adr = [A#vcard_adr{pcode = Value}]};
|
|
|
|
_ -> V
|
|
|
|
end.
|
2004-07-30 23:09:55 +02:00
|
|
|
|
2006-09-14 04:54:21 +02:00
|
|
|
map_vcard_attr(VCardName, Attributes, Pattern, UD) ->
|
2019-06-14 11:33:26 +02:00
|
|
|
Res = lists:filter(
|
|
|
|
fun({Name, _}) ->
|
|
|
|
eldap_utils:case_insensitive_match(Name, VCardName)
|
|
|
|
end, Pattern),
|
2006-09-14 04:54:21 +02:00
|
|
|
case Res of
|
2019-06-14 11:33:26 +02:00
|
|
|
[{_, [{Str, Attrs}|_]}] ->
|
2013-03-14 10:33:02 +01:00
|
|
|
process_pattern(Str, UD,
|
|
|
|
[eldap_utils:get_ldap_attr(X, Attributes)
|
|
|
|
|| X <- Attrs]);
|
|
|
|
_ -> <<"">>
|
2006-09-14 04:54:21 +02:00
|
|
|
end.
|
2004-07-30 23:09:55 +02:00
|
|
|
|
2006-09-23 06:28:03 +02:00
|
|
|
process_pattern(Str, {User, Domain}, AttrValues) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
eldap_filter:do_sub(Str,
|
|
|
|
[{<<"%u">>, User}, {<<"%d">>, Domain}] ++
|
|
|
|
[{<<"%s">>, V, 1} || V <- AttrValues]).
|
2006-09-14 04:54:21 +02:00
|
|
|
|
2016-07-29 16:39:13 +02:00
|
|
|
default_vcard_map() ->
|
2018-01-23 08:54:52 +01:00
|
|
|
[{<<"NICKNAME">>, [{<<"%u">>, []}]},
|
|
|
|
{<<"FN">>, [{<<"%s">>, [<<"displayName">>]}]},
|
|
|
|
{<<"FAMILY">>, [{<<"%s">>, [<<"sn">>]}]},
|
|
|
|
{<<"GIVEN">>, [{<<"%s">>, [<<"givenName">>]}]},
|
|
|
|
{<<"MIDDLE">>, [{<<"%s">>, [<<"initials">>]}]},
|
|
|
|
{<<"ORGNAME">>, [{<<"%s">>, [<<"o">>]}]},
|
|
|
|
{<<"ORGUNIT">>, [{<<"%s">>, [<<"ou">>]}]},
|
|
|
|
{<<"CTRY">>, [{<<"%s">>, [<<"c">>]}]},
|
|
|
|
{<<"LOCALITY">>, [{<<"%s">>, [<<"l">>]}]},
|
|
|
|
{<<"STREET">>, [{<<"%s">>, [<<"street">>]}]},
|
|
|
|
{<<"REGION">>, [{<<"%s">>, [<<"st">>]}]},
|
|
|
|
{<<"PCODE">>, [{<<"%s">>, [<<"postalCode">>]}]},
|
|
|
|
{<<"TITLE">>, [{<<"%s">>, [<<"title">>]}]},
|
|
|
|
{<<"URL">>, [{<<"%s">>, [<<"labeleduri">>]}]},
|
|
|
|
{<<"DESC">>, [{<<"%s">>, [<<"description">>]}]},
|
|
|
|
{<<"TEL">>, [{<<"%s">>, [<<"telephoneNumber">>]}]},
|
|
|
|
{<<"EMAIL">>, [{<<"%s">>, [<<"mail">>]}]},
|
|
|
|
{<<"BDAY">>, [{<<"%s">>, [<<"birthDay">>]}]},
|
|
|
|
{<<"ROLE">>, [{<<"%s">>, [<<"employeeType">>]}]},
|
|
|
|
{<<"PHOTO">>, [{<<"%s">>, [<<"jpegPhoto">>]}]}].
|
2016-07-29 16:39:13 +02:00
|
|
|
|
|
|
|
default_search_fields() ->
|
2017-09-24 11:42:35 +02:00
|
|
|
[{?T("User"), <<"%u">>},
|
|
|
|
{?T("Full Name"), <<"displayName">>},
|
|
|
|
{?T("Given Name"), <<"givenName">>},
|
|
|
|
{?T("Middle Name"), <<"initials">>},
|
|
|
|
{?T("Family Name"), <<"sn">>},
|
|
|
|
{?T("Nickname"), <<"%u">>},
|
|
|
|
{?T("Birthday"), <<"birthDay">>},
|
|
|
|
{?T("Country"), <<"c">>},
|
|
|
|
{?T("City"), <<"l">>},
|
|
|
|
{?T("Email"), <<"mail">>},
|
|
|
|
{?T("Organization Name"), <<"o">>},
|
|
|
|
{?T("Organization Unit"), <<"ou">>}].
|
2016-07-29 16:39:13 +02:00
|
|
|
|
|
|
|
default_search_reported() ->
|
2017-09-24 11:42:35 +02:00
|
|
|
[{?T("Full Name"), <<"FN">>},
|
|
|
|
{?T("Given Name"), <<"FIRST">>},
|
|
|
|
{?T("Middle Name"), <<"MIDDLE">>},
|
|
|
|
{?T("Family Name"), <<"LAST">>},
|
|
|
|
{?T("Nickname"), <<"NICK">>},
|
|
|
|
{?T("Birthday"), <<"BDAY">>},
|
|
|
|
{?T("Country"), <<"CTRY">>},
|
|
|
|
{?T("City"), <<"LOCALITY">>},
|
|
|
|
{?T("Email"), <<"EMAIL">>},
|
|
|
|
{?T("Organization Name"), <<"ORGNAME">>},
|
|
|
|
{?T("Organization Unit"), <<"ORGUNIT">>}].
|
2004-07-30 23:09:55 +02:00
|
|
|
|
2006-09-14 04:54:21 +02:00
|
|
|
parse_options(Host, Opts) ->
|
2019-06-14 11:33:26 +02:00
|
|
|
MyHosts = gen_mod:get_opt_hosts(Opts),
|
|
|
|
Search = mod_vcard_opt:search(Opts),
|
|
|
|
Matches = mod_vcard_opt:matches(Opts),
|
2017-04-11 12:13:58 +02:00
|
|
|
Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?PROCNAME)),
|
2019-06-14 11:33:26 +02:00
|
|
|
Cfg = ?eldap_config(mod_vcard_ldap_opt, Opts),
|
|
|
|
UIDsTemp = mod_vcard_ldap_opt:ldap_uids(Opts),
|
2013-03-14 10:33:02 +01:00
|
|
|
UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp),
|
|
|
|
SubFilter = eldap_utils:generate_subfilter(UIDs),
|
2019-06-14 11:33:26 +02:00
|
|
|
UserFilter = case mod_vcard_ldap_opt:ldap_filter(Opts) of
|
2013-03-14 10:33:02 +01:00
|
|
|
<<"">> ->
|
|
|
|
SubFilter;
|
|
|
|
F ->
|
|
|
|
<<"(&", SubFilter/binary, F/binary, ")">>
|
|
|
|
end,
|
|
|
|
{ok, SearchFilter} =
|
|
|
|
eldap_filter:parse(eldap_filter:do_sub(UserFilter,
|
|
|
|
[{<<"%u">>, <<"*">>}])),
|
2019-06-14 11:33:26 +02:00
|
|
|
VCardMap = mod_vcard_ldap_opt:ldap_vcard_map(Opts),
|
|
|
|
SearchFields = mod_vcard_ldap_opt:ldap_search_fields(Opts),
|
|
|
|
SearchReported = mod_vcard_ldap_opt:ldap_search_reported(Opts),
|
2013-03-14 10:33:02 +01:00
|
|
|
UIDAttrs = [UAttr || {UAttr, _} <- UIDs],
|
2019-06-14 11:33:26 +02:00
|
|
|
VCardMapAttrs = lists:usort(
|
|
|
|
lists:flatten(
|
|
|
|
lists:map(
|
|
|
|
fun({_, Map}) ->
|
|
|
|
[Attrs || {_, Attrs} <- Map]
|
|
|
|
end, VCardMap) ++ UIDAttrs)),
|
|
|
|
SearchReportedAttrs = lists:usort(
|
|
|
|
lists:flatten(
|
|
|
|
lists:map(
|
|
|
|
fun ({_, N}) ->
|
|
|
|
case lists:keyfind(N, 1, VCardMap) of
|
|
|
|
{_, Map} ->
|
|
|
|
[Attrs || {_, Attrs} <- Map];
|
|
|
|
false ->
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
end, SearchReported) ++ UIDAttrs)),
|
2017-08-08 16:46:26 +02:00
|
|
|
#state{serverhost = Host, myhosts = MyHosts,
|
2013-03-14 10:33:02 +01:00
|
|
|
eldap_id = Eldap_ID, search = Search,
|
|
|
|
servers = Cfg#eldap_config.servers,
|
|
|
|
backups = Cfg#eldap_config.backups,
|
|
|
|
port = Cfg#eldap_config.port,
|
|
|
|
tls_options = Cfg#eldap_config.tls_options,
|
|
|
|
dn = Cfg#eldap_config.dn,
|
|
|
|
password = Cfg#eldap_config.password,
|
|
|
|
base = Cfg#eldap_config.base,
|
|
|
|
deref_aliases = Cfg#eldap_config.deref_aliases,
|
|
|
|
uids = UIDs, vcard_map = VCardMap,
|
2006-09-14 04:54:21 +02:00
|
|
|
vcard_map_attrs = VCardMapAttrs,
|
2013-03-14 10:33:02 +01:00
|
|
|
user_filter = UserFilter, search_filter = SearchFilter,
|
2006-09-14 04:54:21 +02:00
|
|
|
search_fields = SearchFields,
|
|
|
|
search_reported = SearchReported,
|
2007-01-27 17:40:37 +01:00
|
|
|
search_reported_attrs = SearchReportedAttrs,
|
2013-03-14 10:33:02 +01:00
|
|
|
matches = Matches}.
|
|
|
|
|
2015-06-01 14:38:27 +02:00
|
|
|
mod_opt_type(ldap_search_fields) ->
|
2019-06-14 11:33:26 +02:00
|
|
|
econf:map(
|
|
|
|
econf:binary(),
|
|
|
|
econf:binary());
|
2015-06-01 14:38:27 +02:00
|
|
|
mod_opt_type(ldap_search_reported) ->
|
2019-06-14 11:33:26 +02:00
|
|
|
econf:map(
|
|
|
|
econf:binary(),
|
|
|
|
econf:binary());
|
2015-06-01 14:38:27 +02:00
|
|
|
mod_opt_type(ldap_vcard_map) ->
|
2019-06-14 11:33:26 +02:00
|
|
|
econf:map(
|
|
|
|
econf:binary(),
|
|
|
|
econf:map(
|
|
|
|
econf:binary(),
|
|
|
|
econf:list(
|
|
|
|
econf:binary())));
|
|
|
|
mod_opt_type(ldap_backups) ->
|
|
|
|
econf:list(econf:domain(), [unique]);
|
|
|
|
mod_opt_type(ldap_base) ->
|
|
|
|
econf:binary();
|
|
|
|
mod_opt_type(ldap_deref_aliases) ->
|
|
|
|
econf:enum([never, searching, finding, always]);
|
|
|
|
mod_opt_type(ldap_encrypt) ->
|
|
|
|
econf:enum([tls, starttls, none]);
|
|
|
|
mod_opt_type(ldap_filter) ->
|
|
|
|
econf:ldap_filter();
|
|
|
|
mod_opt_type(ldap_password) ->
|
|
|
|
econf:binary();
|
|
|
|
mod_opt_type(ldap_port) ->
|
|
|
|
econf:port();
|
|
|
|
mod_opt_type(ldap_rootdn) ->
|
|
|
|
econf:binary();
|
|
|
|
mod_opt_type(ldap_servers) ->
|
|
|
|
econf:list(econf:domain(), [unique]);
|
|
|
|
mod_opt_type(ldap_tls_cacertfile) ->
|
|
|
|
econf:pem();
|
|
|
|
mod_opt_type(ldap_tls_certfile) ->
|
|
|
|
econf:pem();
|
|
|
|
mod_opt_type(ldap_tls_depth) ->
|
|
|
|
econf:non_neg_int();
|
|
|
|
mod_opt_type(ldap_tls_verify) ->
|
|
|
|
econf:enum([hard, soft, false]);
|
|
|
|
mod_opt_type(ldap_uids) ->
|
|
|
|
econf:either(
|
|
|
|
econf:list(
|
|
|
|
econf:and_then(
|
|
|
|
econf:binary(),
|
|
|
|
fun(U) -> {U, <<"%u">>} end)),
|
|
|
|
econf:map(econf:binary(), econf:binary(), [unique])).
|
|
|
|
|
|
|
|
-spec mod_options(binary()) -> [{ldap_uids, [{binary(), binary()}]} |
|
|
|
|
{atom(), any()}].
|
2018-01-23 08:54:52 +01:00
|
|
|
mod_options(Host) ->
|
|
|
|
[{ldap_search_fields, default_search_fields()},
|
|
|
|
{ldap_search_reported, default_search_reported()},
|
2019-06-14 11:33:26 +02:00
|
|
|
{ldap_vcard_map, default_vcard_map()},
|
|
|
|
{ldap_backups, ejabberd_option:ldap_backups(Host)},
|
|
|
|
{ldap_base, ejabberd_option:ldap_base(Host)},
|
|
|
|
{ldap_uids, ejabberd_option:ldap_uids(Host)},
|
|
|
|
{ldap_deref_aliases, ejabberd_option:ldap_deref_aliases(Host)},
|
|
|
|
{ldap_encrypt, ejabberd_option:ldap_encrypt(Host)},
|
|
|
|
{ldap_password, ejabberd_option:ldap_password(Host)},
|
|
|
|
{ldap_port, ejabberd_option:ldap_port(Host)},
|
|
|
|
{ldap_rootdn, ejabberd_option:ldap_rootdn(Host)},
|
|
|
|
{ldap_servers, ejabberd_option:ldap_servers(Host)},
|
|
|
|
{ldap_filter, ejabberd_option:ldap_filter(Host)},
|
|
|
|
{ldap_tls_certfile, ejabberd_option:ldap_tls_certfile(Host)},
|
|
|
|
{ldap_tls_cacertfile, ejabberd_option:ldap_tls_cacertfile(Host)},
|
|
|
|
{ldap_tls_depth, ejabberd_option:ldap_tls_depth(Host)},
|
|
|
|
{ldap_tls_verify, ejabberd_option:ldap_tls_verify(Host)}].
|
2020-01-08 10:24:51 +01:00
|
|
|
|
|
|
|
mod_doc() ->
|
|
|
|
#{opts =>
|
|
|
|
[{ldap_search_fields,
|
|
|
|
#{value => "{Name: Attribute, ...}",
|
|
|
|
desc =>
|
|
|
|
?T("This option defines the search form and the LDAP "
|
|
|
|
"attributes to search within. 'Name' is the name of a "
|
|
|
|
"search form field which will be automatically "
|
|
|
|
"translated by using the translation files "
|
|
|
|
"(see 'msgs/*.msg' for available words). "
|
|
|
|
"'Attribute' is the LDAP attribute or the pattern '%u'."),
|
|
|
|
example =>
|
|
|
|
[{?T("The default is:"),
|
|
|
|
["User: \"%u\"",
|
|
|
|
"\"Full Name\": displayName",
|
|
|
|
"\"Given Name\": givenName",
|
|
|
|
"\"Middle Name\": initials",
|
|
|
|
"\"Family Name\": sn",
|
|
|
|
"Nickname: \"%u\"",
|
|
|
|
"Birthday: birthDay",
|
|
|
|
"Country: c",
|
|
|
|
"City: l",
|
|
|
|
"Email: mail",
|
|
|
|
"\"Organization Name\": o",
|
|
|
|
"\"Organization Unit\": ou"]
|
|
|
|
}]}},
|
|
|
|
{ldap_search_reported,
|
|
|
|
#{value => "{SearchField: VcardField}, ...}",
|
|
|
|
desc =>
|
|
|
|
?T("This option defines which search fields should be "
|
|
|
|
"reported. 'SearchField' is the name of a search form "
|
|
|
|
"field which will be automatically translated by using "
|
|
|
|
"the translation files (see 'msgs/*.msg' for available "
|
|
|
|
"words). 'VcardField' is the vCard field name defined "
|
|
|
|
"in the 'ldap_vcard_map' option."),
|
|
|
|
example =>
|
|
|
|
[{?T("The default is:"),
|
|
|
|
["\"Full Name\": FN",
|
|
|
|
"\"Given Name\": FIRST",
|
|
|
|
"\"Middle Name\": MIDDLE",
|
|
|
|
"\"Family Name\": LAST",
|
|
|
|
"\"Nickname\": NICKNAME",
|
|
|
|
"\"Birthday\": BDAY",
|
|
|
|
"\"Country\": CTRY",
|
|
|
|
"\"City\": LOCALITY",
|
|
|
|
"\"Email\": EMAIL",
|
|
|
|
"\"Organization Name\": ORGNAME",
|
|
|
|
"\"Organization Unit\": ORGUNIT"]
|
|
|
|
}]}},
|
|
|
|
{ldap_vcard_map,
|
|
|
|
#{value => "{Name: {Pattern, LDAPattributes}, ...}",
|
|
|
|
desc =>
|
|
|
|
?T("With this option you can set the table that maps LDAP "
|
|
|
|
"attributes to vCard fields. 'Name' is the type name of "
|
|
|
|
"the vCard as defined in "
|
|
|
|
"http://tools.ietf.org/html/rfc2426[RFC 2426]. "
|
|
|
|
"'Pattern' is a string which contains "
|
|
|
|
"pattern variables '%u', '%d' or '%s'. "
|
|
|
|
"'LDAPattributes' is the list containing LDAP attributes. "
|
|
|
|
"The pattern variables '%s' will be sequentially replaced "
|
|
|
|
"with the values of LDAP attributes from "
|
|
|
|
"'List_of_LDAP_attributes', '%u' will be replaced with "
|
|
|
|
"the user part of a JID, and '%d' will be replaced with "
|
|
|
|
"the domain part of a JID."),
|
|
|
|
example =>
|
|
|
|
[{?T("The default is:"),
|
|
|
|
["NICKNAME: {\"%u\": []}",
|
|
|
|
"FN: {\"%s\": [displayName]}",
|
|
|
|
"LAST: {\"%s\": [sn]}",
|
|
|
|
"FIRST: {\"%s\": [givenName]}",
|
|
|
|
"MIDDLE: {\"%s\": [initials]}",
|
|
|
|
"ORGNAME: {\"%s\": [o]}",
|
|
|
|
"ORGUNIT: {\"%s\": [ou]}",
|
|
|
|
"CTRY: {\"%s\": [c]}",
|
|
|
|
"LOCALITY: {\"%s\": [l]}",
|
|
|
|
"STREET: {\"%s\": [street]}",
|
|
|
|
"REGION: {\"%s\": [st]}",
|
|
|
|
"PCODE: {\"%s\": [postalCode]}",
|
|
|
|
"TITLE: {\"%s\": [title]}",
|
|
|
|
"URL: {\"%s\": [labeleduri]}",
|
|
|
|
"DESC: {\"%s\": [description]}",
|
|
|
|
"TEL: {\"%s\": [telephoneNumber]}",
|
|
|
|
"EMAIL: {\"%s\": [mail]}",
|
|
|
|
"BDAY: {\"%s\": [birthDay]}",
|
|
|
|
"ROLE: {\"%s\": [employeeType]}",
|
|
|
|
"PHOTO: {\"%s\": [jpegPhoto]}"]
|
|
|
|
}]}}] ++
|
|
|
|
[{Opt,
|
|
|
|
#{desc =>
|
|
|
|
{?T("Same as top-level '~s' option, but "
|
|
|
|
"applied to this module only."), [Opt]}}}
|
|
|
|
|| Opt <- [ldap_base, ldap_servers, ldap_uids,
|
|
|
|
ldap_deref_aliases, ldap_encrypt, ldap_password,
|
|
|
|
ldap_port, ldap_rootdn, ldap_filter,
|
|
|
|
ldap_tls_certfile, ldap_tls_cacertfile,
|
|
|
|
ldap_tls_depth, ldap_tls_verify, ldap_backups]]}.
|