aboutsummaryrefslogtreecommitdiff
path: root/src/mod_shared_roster_ldap.erl
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2010-11-05 02:34:45 +0900
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2010-11-05 02:34:45 +0900
commit31757116fc9af00a16c22793b604aa062a5080e8 (patch)
tree471bb16fa630075742ca20a77c17b0ae4b975736 /src/mod_shared_roster_ldap.erl
parentFixes a leak of ejabberd_receiver processes. (diff)
LDAP shared roster support (thanks to Realloc and Marcin Owsiany)
Diffstat (limited to 'src/mod_shared_roster_ldap.erl')
-rw-r--r--src/mod_shared_roster_ldap.erl672
1 files changed, 672 insertions, 0 deletions
diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl
new file mode 100644
index 000000000..b912a5e72
--- /dev/null
+++ b/src/mod_shared_roster_ldap.erl
@@ -0,0 +1,672 @@
+%%%-------------------------------------------------------------------
+%%% File : mod_shared_roster_ldap.erl
+%%% Author : Realloc <realloc@realloc.spb.ru>
+%%% Marcin Owsiany <marcin@owsiany.pl>
+%%% Evgeniy Khramtsov <ekhramtsov@process-one.net>
+%%% Description : LDAP shared roster management
+%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@process-one.net>
+%%%
+%%%
+%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
+%%%
+%%% 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., 59 Temple Place, Suite 330, Boston, MA
+%%% 02111-1307 USA
+%%%
+%%%-------------------------------------------------------------------
+-module(mod_shared_roster_ldap).
+
+-behaviour(gen_server).
+-behaviour(gen_mod).
+
+%% API
+-export([start_link/2, start/2, stop/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-export([get_user_roster/2,
+ get_subscription_lists/3,
+ get_jid_info/4,
+ process_item/2,
+ in_subscription/6,
+ out_subscription/4]).
+
+-include("ejabberd.hrl").
+-include("mod_roster.hrl").
+-include("eldap/eldap.hrl").
+
+-define(CACHE_SIZE, 1000).
+-define(USER_CACHE_VALIDITY, 300). %% in seconds
+-define(GROUP_CACHE_VALIDITY, 300). %% in seconds
+-define(LDAP_SEARCH_TIMEOUT, 5). %% Timeout for LDAP search queries in seconds
+
+-record(state, {host,
+ eldap_id,
+ servers,
+ backups,
+ port,
+ tls_options,
+ dn,
+ base,
+ password,
+ uid,
+ group_attr,
+ group_desc,
+ user_desc,
+ user_uid,
+ uid_format,
+ uid_format_re,
+ filter,
+ ufilter,
+ rfilter,
+ gfilter,
+ auth_check,
+ user_cache_size,
+ group_cache_size,
+ user_cache_validity,
+ group_cache_validity}).
+
+-record(group_info, {desc, members}).
+
+%%====================================================================
+%% API
+%%====================================================================
+start_link(Host, Opts) ->
+ Proc = gen_mod:get_module_proc(Host, ?MODULE),
+ gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
+
+start(Host, Opts) ->
+ Proc = gen_mod:get_module_proc(Host, ?MODULE),
+ ChildSpec = {
+ Proc, {?MODULE, start_link, [Host, Opts]},
+ permanent, 1000, worker, [?MODULE]
+ },
+ supervisor:start_child(ejabberd_sup, ChildSpec).
+
+stop(Host) ->
+ Proc = gen_mod:get_module_proc(Host, ?MODULE),
+ supervisor:terminate_child(ejabberd_sup, Proc),
+ supervisor:delete_child(ejabberd_sup, Proc).
+
+%%--------------------------------------------------------------------
+%% Hooks
+%%--------------------------------------------------------------------
+get_user_roster(Items, {U, S} = US) ->
+ SRUsers = get_user_to_groups_map(US, true),
+ %% If partially subscribed users are also in shared roster,
+ %% show them as totally subscribed:
+ {NewItems1, SRUsersRest} =
+ lists:mapfoldl(
+ fun(Item, SRUsers1) ->
+ {_, _, {U1, S1, _}} = Item#roster.usj,
+ US1 = {U1, S1},
+ case dict:find(US1, SRUsers1) of
+ {ok, _GroupNames} ->
+ {Item#roster{subscription = both, ask = none},
+ dict:erase(US1, SRUsers1)};
+ error ->
+ {Item, SRUsers1}
+ end
+ end, SRUsers, Items),
+ %% Export items in roster format:
+ SRItems = [#roster{usj = {U, S, {U1, S1, ""}},
+ us = US,
+ jid = {U1, S1, ""},
+ name = get_user_name(U1,S1),
+ subscription = both,
+ ask = none,
+ groups = GroupNames} ||
+ {{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
+ SRItems ++ NewItems1.
+
+%% This function in use to rewrite the roster entries when moving or renaming
+%% them in the user contact list.
+process_item(RosterItem, _Host) ->
+ USFrom = RosterItem#roster.us,
+ {User,Server,_Resource} = RosterItem#roster.jid,
+ USTo = {User,Server},
+ Map = get_user_to_groups_map(USFrom, false),
+ case dict:find(USTo, Map) of
+ error ->
+ RosterItem;
+ {ok, []} ->
+ RosterItem;
+ {ok, GroupNames} when RosterItem#roster.subscription == remove ->
+ %% Roster item cannot be removed:
+ %% We simply reset the original groups:
+ RosterItem#roster{subscription = both, ask = none,
+ groups=GroupNames};
+ _ ->
+ RosterItem#roster{subscription = both, ask = none}
+ end.
+
+get_subscription_lists({F, T}, User, Server) ->
+ LUser = jlib:nodeprep(User),
+ LServer = jlib:nameprep(Server),
+ US = {LUser, LServer},
+ DisplayedGroups = get_user_displayed_groups(US),
+ SRUsers =
+ lists:usort(
+ lists:flatmap(
+ fun(Group) ->
+ get_group_users(LServer, Group)
+ end, DisplayedGroups)),
+ SRJIDs = [{U1, S1, ""} || {U1, S1} <- SRUsers],
+ {lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}.
+
+get_jid_info({Subscription, Groups}, User, Server, JID) ->
+ LUser = jlib:nodeprep(User),
+ LServer = jlib:nameprep(Server),
+ US = {LUser, LServer},
+ {U1, S1, _} = jlib:jid_tolower(JID),
+ US1 = {U1, S1},
+ SRUsers = get_user_to_groups_map(US, false),
+ case dict:find(US1, SRUsers) of
+ {ok, GroupNames} ->
+ NewGroups = if
+ Groups == [] -> GroupNames;
+ true -> Groups
+ end,
+ {both, NewGroups};
+ error ->
+ {Subscription, Groups}
+ end.
+
+in_subscription(Acc, User, Server, JID, Type, _Reason) ->
+ process_subscription(in, User, Server, JID, Type, Acc).
+
+out_subscription(User, Server, JID, Type) ->
+ process_subscription(out, User, Server, JID, Type, false).
+
+process_subscription(Direction, User, Server, JID, _Type, Acc) ->
+ LUser = jlib:nodeprep(User),
+ LServer = jlib:nameprep(Server),
+ US = {LUser, LServer},
+ {U1, S1, _} = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
+ US1 = {U1, S1},
+ DisplayedGroups = get_user_displayed_groups(US),
+ SRUsers =
+ lists:usort(
+ lists:flatmap(
+ fun(Group) ->
+ get_group_users(LServer, Group)
+ end, DisplayedGroups)),
+ case lists:member(US1, SRUsers) of
+ true ->
+ case Direction of
+ in ->
+ {stop, false};
+ out ->
+ stop
+ end;
+ false ->
+ Acc
+ end.
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+init([Host, Opts]) ->
+ State = parse_options(Host, Opts),
+ cache_tab:new(shared_roster_ldap_user,
+ [{max_size, State#state.user_cache_size},
+ {life_time, State#state.user_cache_validity}]),
+ cache_tab:new(shared_roster_ldap_group,
+ [{max_size, State#state.group_cache_size},
+ {life_time, State#state.group_cache_validity}]),
+ ejabberd_hooks:add(roster_get, Host,
+ ?MODULE, get_user_roster, 70),
+ ejabberd_hooks:add(roster_in_subscription, Host,
+ ?MODULE, in_subscription, 30),
+ ejabberd_hooks:add(roster_out_subscription, Host,
+ ?MODULE, out_subscription, 30),
+ ejabberd_hooks:add(roster_get_subscription_lists, Host,
+ ?MODULE, get_subscription_lists, 70),
+ ejabberd_hooks:add(roster_get_jid_info, Host,
+ ?MODULE, get_jid_info, 70),
+ ejabberd_hooks:add(roster_process_item, Host,
+ ?MODULE, process_item, 50),
+ eldap_pool:start_link(State#state.eldap_id,
+ State#state.servers,
+ State#state.backups,
+ State#state.port,
+ State#state.dn,
+ State#state.password,
+ State#state.tls_options),
+ {ok, State}.
+
+handle_call(get_state, _From, State) ->
+ {reply, {ok, State}, State};
+
+handle_call(_Request, _From, State) ->
+ {reply, {error, badarg}, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, State) ->
+ Host = State#state.host,
+ ejabberd_hooks:delete(roster_get, Host,
+ ?MODULE, get_user_roster, 70),
+ ejabberd_hooks:delete(roster_in_subscription, Host,
+ ?MODULE, in_subscription, 30),
+ ejabberd_hooks:delete(roster_out_subscription, Host,
+ ?MODULE, out_subscription, 30),
+ ejabberd_hooks:delete(roster_get_subscription_lists, Host,
+ ?MODULE, get_subscription_lists, 70),
+ ejabberd_hooks:delete(roster_get_jid_info, Host,
+ ?MODULE, get_jid_info, 70),
+ ejabberd_hooks:delete(roster_process_item, Host,
+ ?MODULE, process_item, 50).
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+%% For a given user, map all his shared roster contacts to groups they are
+%% members of. Skip the user himself iff SkipUS is true.
+get_user_to_groups_map({_, Server} = US, SkipUS) ->
+ DisplayedGroups = get_user_displayed_groups(US),
+ lists:foldl(
+ fun(Group, Dict1) ->
+ GroupName = get_group_name(Server, Group),
+ lists:foldl(
+ fun(Contact, Dict) ->
+ if SkipUS, Contact == US ->
+ Dict;
+ true ->
+ dict:append(Contact, GroupName, Dict)
+ end
+ end, Dict1, get_group_users(Server, Group))
+ end, dict:new(), DisplayedGroups).
+
+%% Pass given FilterParseArgs to eldap_filter:parse, and if successful, run and
+%% return the resulting filter, retrieving given AttributesList. Return the
+%% result entries. On any error silently return an empty list of results.
+%%
+%% Eldap server ID and base DN for the query are both retrieved from the State
+%% record.
+eldap_search(State, FilterParseArgs, AttributesList) ->
+ case apply(eldap_filter, parse, FilterParseArgs) of
+ {ok, EldapFilter} ->
+ %% Filter parsing succeeded
+ case eldap_pool:search(State#state.eldap_id,
+ [{base, State#state.base},
+ {filter, EldapFilter},
+ {timeout, ?LDAP_SEARCH_TIMEOUT},
+ {attributes, AttributesList}]) of
+ #eldap_search_result{entries = Es} ->
+ %% A result with entries. Return their list.
+ Es;
+ _ ->
+ %% Something else. Pretend we got no results.
+ []
+ end;
+ _ ->
+ %% Filter parsing failed. Pretend we got no results.
+ []
+ end.
+
+get_user_displayed_groups({_User, Host}) ->
+ {ok, State} = eldap_utils:get_state(Host, ?MODULE),
+ GroupAttr = State#state.group_attr,
+ Entries = eldap_search(State, [State#state.rfilter], [GroupAttr]),
+ Reply = lists:flatmap(
+ fun(#eldap_entry{attributes = Attrs}) ->
+ case Attrs of
+ [{GroupAttr, ValuesList}] ->
+ ValuesList;
+ _ ->
+ []
+ end
+ end, Entries),
+ lists:usort(Reply).
+
+get_group_users(Host, Group) ->
+ {ok, State} = eldap_utils:get_state(Host, ?MODULE),
+ case cache_tab:dirty_lookup(
+ shared_roster_ldap_group,
+ {Group, Host},
+ fun() -> search_group_info(State, Group) end) of
+ {ok, #group_info{members = Members}} when Members /= undefined ->
+ Members;
+ _ ->
+ []
+ end.
+
+get_group_name(Host, Group) ->
+ {ok, State} = eldap_utils:get_state(Host, ?MODULE),
+ case cache_tab:dirty_lookup(
+ shared_roster_ldap_group,
+ {Group, Host},
+ fun() -> search_group_info(State, Group) end) of
+ {ok, #group_info{desc = GroupName}} when GroupName /= undefined ->
+ GroupName;
+ _ ->
+ Group
+ end.
+
+get_user_name(User, Host) ->
+ {ok, State} = eldap_utils:get_state(Host, ?MODULE),
+ case cache_tab:dirty_lookup(
+ shared_roster_ldap_user,
+ {User, Host},
+ fun() -> search_user_name(State, User) end) of
+ {ok, UserName} ->
+ UserName;
+ error ->
+ User
+ end.
+
+search_group_info(State, Group) ->
+ Extractor =
+ case State#state.uid_format_re of
+ "" ->
+ fun(UID) ->
+ catch eldap_utils:get_user_part(UID, State#state.uid_format)
+ end;
+ _ ->
+ fun(UID) ->
+ catch get_user_part_re(UID, State#state.uid_format_re)
+ end
+ end,
+ AuthChecker = case State#state.auth_check of
+ true -> fun ejabberd_auth:is_user_exists/2;
+ _ -> fun(_U, _S) -> true end
+ end,
+ Host = State#state.host,
+ case eldap_search(
+ State,
+ [eldap_filter:do_sub(State#state.gfilter, [{"%g", Group}])],
+ [State#state.group_attr, State#state.group_desc, State#state.uid]) of
+ [] ->
+ error;
+ LDAPEntries ->
+ {GroupDesc, MembersLists} =
+ lists:foldl(
+ fun(#eldap_entry{attributes=Attrs}, {DescAcc, JIDsAcc}) ->
+ case {eldap_utils:get_ldap_attr(State#state.group_attr, Attrs),
+ eldap_utils:get_ldap_attr(State#state.group_desc, Attrs),
+ lists:keysearch(State#state.uid, 1, Attrs)} of
+ {ID, Desc, {value, {GroupMemberAttr, Members}}}
+ when ID /= "", GroupMemberAttr == State#state.uid ->
+ JIDs = lists:foldl(
+ fun({ok, UID}, L) ->
+ PUID = jlib:nodeprep(UID),
+ case PUID of
+ error -> L;
+ _ ->
+ case AuthChecker(PUID, Host) of
+ true -> [{PUID, Host} | L];
+ _ -> L
+ end
+ end;
+ (_, L) ->
+ L
+ end, [], lists:map(Extractor, Members)),
+ {Desc, [JIDs|JIDsAcc]};
+ _ ->
+ {DescAcc, JIDsAcc}
+ end
+ end, {Group, []}, LDAPEntries),
+ {ok, #group_info{desc = GroupDesc,
+ members = lists:usort(lists:flatten(MembersLists))}}
+ end.
+
+search_user_name(State, User) ->
+ case eldap_search(
+ State,
+ [eldap_filter:do_sub(State#state.ufilter, [{"%u", User}])],
+ [State#state.user_desc, State#state.user_uid]) of
+ [#eldap_entry{attributes=Attrs}|_] ->
+ case {eldap_utils:get_ldap_attr(State#state.user_uid, Attrs),
+ eldap_utils:get_ldap_attr(State#state.user_desc, Attrs)} of
+ {UID, Desc} when UID /= "" ->
+ %% By returning "" get_ldap_attr means "not found"
+ {ok, Desc};
+ _ ->
+ error
+ end;
+ [] ->
+ error
+ end.
+
+%% Getting User ID part by regex pattern
+get_user_part_re(String, Pattern) ->
+ case catch re:run(String, Pattern) of
+ {match, Captured} ->
+ {First, Len} = lists:nth(2,Captured),
+ Result = string:sub_string(String, First+1, First+Len),
+ {ok,Result};
+ _ -> {error, badmatch}
+ end.
+
+parse_options(Host, Opts) ->
+ Eldap_ID = atom_to_list(gen_mod:get_module_proc(Host, ?MODULE)),
+ LDAPServers = case gen_mod:get_opt(ldap_servers, Opts, undefined) of
+ undefined ->
+ ejabberd_config:get_local_option({ldap_servers, Host});
+ S -> S
+ end,
+ LDAPBackups = case gen_mod:get_opt(ldap_backups, Opts, undefined) of
+ undefined ->
+ ejabberd_config:get_local_option({ldap_servers, Host});
+ Backups -> Backups
+ end,
+ LDAPEncrypt = case gen_mod:get_opt(ldap_encrypt, Opts, undefined) of
+ undefined ->
+ ejabberd_config:get_local_option({ldap_encrypt, Host});
+ E -> E
+ end,
+ LDAPTLSVerify = case gen_mod:get_opt(ldap_tls_verify, Opts, undefined) of
+ undefined ->
+ ejabberd_config:get_local_option({ldap_tls_verify, Host});
+ Verify -> Verify
+ end,
+ LDAPPort = case gen_mod:get_opt(ldap_port, Opts, undefined) of
+ undefined ->
+ case ejabberd_config:get_local_option({ldap_port, Host}) of
+ undefined -> case LDAPEncrypt of
+ tls -> ?LDAPS_PORT;
+ starttls -> ?LDAP_PORT;
+ _ -> ?LDAP_PORT
+ end;
+ P -> P
+ end;
+ P -> P
+ end,
+ LDAPBase = case gen_mod:get_opt(ldap_base, Opts, undefined) of
+ undefined ->
+ ejabberd_config:get_local_option({ldap_base, Host});
+ B -> B
+ end,
+ GroupAttr = case gen_mod:get_opt(ldap_groupattr, Opts, undefined) of
+ undefined -> "cn";
+ GA -> GA
+ end,
+ GroupDesc = case gen_mod:get_opt(ldap_groupdesc, Opts, undefined) of
+ undefined -> GroupAttr;
+ GD -> GD
+ end,
+ UserDesc = case gen_mod:get_opt(ldap_userdesc, Opts, undefined) of
+ undefined -> "cn";
+ UD -> UD
+ end,
+ UserUID = case gen_mod:get_opt(ldap_useruid, Opts, undefined) of
+ undefined -> "cn";
+ UU -> UU
+ end,
+ UIDAttr = case gen_mod:get_opt(ldap_memberattr, Opts, undefined) of
+ undefined -> "memberUid";
+ UA -> UA
+ end,
+ UIDAttrFormat = case gen_mod:get_opt(ldap_memberattr_format, Opts, undefined) of
+ undefined -> "%u";
+ UAF -> UAF
+ end,
+ UIDAttrFormatRe =
+ case gen_mod:get_opt(ldap_memberattr_format_re, Opts, undefined) of
+ undefined -> "";
+ UAFre -> case catch re:compile(UAFre) of
+ {ok, MP} ->
+ MP;
+ _ ->
+ ?ERROR_MSG("Invalid ldap_memberattr_format_re '~s' "
+ "or no RE support in this erlang version. "
+ "ldap_memberattr_format '~s' will be used "
+ "instead.", [UAFre, UIDAttrFormat]),
+ ""
+ end
+ end,
+ AuthCheck = case gen_mod:get_opt(ldap_auth_check, Opts, undefined) of
+ undefined -> true;
+ on -> true;
+ AC -> AC
+ end,
+ RootDN = case gen_mod:get_opt(ldap_rootdn, Opts, undefined) of
+ undefined ->
+ case ejabberd_config:get_local_option({ldap_rootdn, Host}) of
+ undefined -> "";
+ RDN -> RDN
+ end;
+ RDN -> RDN
+ end,
+ Password = case gen_mod:get_opt(ldap_password, Opts, undefined) of
+ undefined ->
+ case ejabberd_config:get_local_option({ldap_password, Host}) of
+ undefined -> "";
+ Pass -> Pass
+ end;
+ Pass -> Pass
+ end,
+ UserCacheValidity =
+ case gen_mod:get_opt(ldap_user_cache_validity, Opts, undefined) of
+ undefined ->
+ case ejabberd_config:get_local_option({ldap_user_cache_validity, Host}) of
+ undefined -> ?USER_CACHE_VALIDITY;
+ UVSeconds -> UVSeconds
+ end;
+ UVSeconds -> UVSeconds
+ end,
+ GroupCacheValidity =
+ case gen_mod:get_opt(ldap_group_cache_validity, Opts, undefined) of
+ undefined ->
+ case ejabberd_config:get_local_option({ldap_group_cache_validity, Host}) of
+ undefined -> ?GROUP_CACHE_VALIDITY;
+ GVSeconds -> GVSeconds
+ end;
+ GVSeconds -> GVSeconds
+ end,
+ UserCacheSize =
+ case gen_mod:get_opt(ldap_user_cache_size, Opts, undefined) of
+ undefined ->
+ case ejabberd_config:get_local_option({ldap_user_cache_size, Host}) of
+ undefined -> ?CACHE_SIZE;
+ USSeconds -> USSeconds
+ end;
+ USSeconds -> USSeconds
+ end,
+ GroupCacheSize =
+ case gen_mod:get_opt(ldap_group_cache_size, Opts, undefined) of
+ undefined ->
+ case ejabberd_config:get_local_option({ldap_group_cache_size, Host}) of
+ undefined -> ?CACHE_SIZE;
+ GSSeconds -> GSSeconds
+ end;
+ GSSeconds -> GSSeconds
+ end,
+ ConfigFilter = case gen_mod:get_opt(ldap_filter, Opts, undefined) of
+ undefined ->
+ ejabberd_config:get_local_option({ldap_filter, Host});
+ F ->
+ F
+ end,
+ ConfigUserFilter = case gen_mod:get_opt(ldap_ufilter, Opts, undefined) of
+ undefined ->
+ ejabberd_config:get_local_option({ldap_ufilter, Host});
+ UF -> UF
+ end,
+
+ ConfigGroupFilter = case gen_mod:get_opt(ldap_gfilter, Opts, undefined) of
+ undefined ->
+ ejabberd_config:get_local_option({ldap_gfilter, Host});
+ GF -> GF
+ end,
+
+ RosterFilter = case gen_mod:get_opt(ldap_rfilter, Opts, undefined) of
+ undefined ->
+ ejabberd_config:get_local_option({ldap_rfilter, Host});
+ RF ->
+ RF
+ end,
+
+ SubFilter = "(&("++UIDAttr++"="++UIDAttrFormat++")("++GroupAttr++"=%g))",
+ UserSubFilter = case ConfigUserFilter of
+ undefined -> eldap_filter:do_sub(SubFilter, [{"%g", "*"}]);
+ "" -> eldap_filter:do_sub(SubFilter, [{"%g", "*"}]);
+ UString -> UString
+ end,
+ GroupSubFilter = case ConfigGroupFilter of
+ undefined -> eldap_filter:do_sub(SubFilter, [{"%u", "*"}]);
+ "" -> eldap_filter:do_sub(SubFilter, [{"%u", "*"}]);
+ GString -> GString
+ end,
+ Filter = case ConfigFilter of
+ undefined -> SubFilter;
+ "" -> SubFilter;
+ _ -> "(&" ++ SubFilter ++ ConfigFilter ++ ")"
+ end,
+ UserFilter = case ConfigFilter of
+ undefined -> UserSubFilter;
+ "" -> UserSubFilter;
+ _ -> "(&" ++ UserSubFilter ++ ConfigFilter ++ ")"
+ end,
+ GroupFilter = case ConfigFilter of
+ undefined -> GroupSubFilter;
+ "" -> GroupSubFilter;
+ _ -> "(&" ++ GroupSubFilter ++ ConfigFilter ++ ")"
+ end,
+ #state{host = Host,
+ eldap_id = Eldap_ID,
+ servers = LDAPServers,
+ backups = LDAPBackups,
+ port = LDAPPort,
+ tls_options = [{encrypt, LDAPEncrypt},
+ {tls_verify, LDAPTLSVerify}],
+ dn = RootDN,
+ base = LDAPBase,
+ password = Password,
+ uid = UIDAttr,
+ group_attr = GroupAttr,
+ group_desc = GroupDesc,
+ user_desc = UserDesc,
+ user_uid = UserUID,
+ uid_format = UIDAttrFormat,
+ uid_format_re = UIDAttrFormatRe,
+ filter = Filter,
+ ufilter = UserFilter,
+ rfilter = RosterFilter,
+ gfilter = GroupFilter,
+ auth_check = AuthCheck,
+ user_cache_size = UserCacheSize,
+ user_cache_validity = UserCacheValidity,
+ group_cache_size = GroupCacheSize,
+ group_cache_validity = GroupCacheValidity}.