match on several alternative attributes.
* src/mod_vcard_ldap.erl: Likewise.
* doc/guide.tex: Updated.
* eldap_utils.erl: Refactoring.
* src/eldap/Makefile.in: Likewise.
SVN Revision: 661
+2006-10-17 Mickael Remond <mickael.remond@process-one.net>
+
+ * src/ejabberd_auth_ldap.erl: LDAP authentication now allows to
+ match on several alternative attributes.
+ * src/mod_vcard_ldap.erl: Likewise.
+ * doc/guide.tex: Updated.
+ * eldap_utils.erl: Refactoring.
+ * src/eldap/Makefile.in: Likewise.
+
2006-10-09 Alexey Shchepin <alexey@sevcom.net>
* src/mod_privacy_odbc.erl: Privacy rules support using odbc
{host_config, "example.com", [{auth_method, ldap},
{ldap_servers, ["localhost"]},
- {ldap_uidattr, "uid"},
+ {ldap_uids, [{"uid"}]},
{ldap_rootdn, "dc=localdomain"},
{ldap_rootdn, "dc=example,dc=com"},
{ldap_password, ""}]}.
{host_config, "example.com", [{auth_method, ldap},
{ldap_servers, ["localhost", "otherhost"]},
- {ldap_uidattr, "uid"},
+ {ldap_uids, [{"uid"}]},
{ldap_rootdn, "dc=localdomain"},
{ldap_rootdn, "dc=example,dc=com"},
{ldap_password, ""}]}.
<A NAME="sec:ldapauth"></A>
You can authenticate users against an LDAP directory. Available options are:
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description">
-<B><TT>ldap_base</TT></B><DD CLASS="dd-description">LDAP base directory which stores users
- accounts. This option is required.
-<DT CLASS="dt-description"><B><TT>ldap_uidattr</TT></B><DD CLASS="dd-description">LDAP attribute which holds
+<B><TT>ldap_base</TT></B><DD CLASS="dd-description">LDAP base directory which stores
+ users accounts. This option is required.
+ <DT CLASS="dt-description"><B><TT>ldap_uids</TT></B><DD CLASS="dd-description">LDAP attribute which holds a list
+ of attributes to use as alternatives for getting the JID. The value is of
+ the form: <TT>[{ldap_uidattr}]</TT> or <TT>[{ldap_uidattr,
+ ldap_uidattr_format}]</TT>. You can use as many comma separated tuples
+ <TT>{ldap_uidattr, ldap_uidattr_format}</TT> that is needed. The default
+ value is <TT>[{"uid", "%u"}]</TT>. The defaut <TT>ldap_uidattr_format</TT>
+ is <TT>"%u"</TT>. The values for <TT>ldap_uidattr</TT> and
+ <TT>ldap_uidattr_format</TT> are described as follow:
+ <DL CLASS="description" COMPACT=compact><DT CLASS="dt-description">
+ <B><TT>ldap_uidattr</TT></B><DD CLASS="dd-description">LDAP attribute which holds
the user's part of a JID. The default value is <TT>"uid"</TT>.
-<DT CLASS="dt-description"><B><TT>ldap_uidattr_format</TT></B><DD CLASS="dd-description">Format of the
- <TT>ldap_uidattr</TT> variable. The format <EM>must</EM> contain one and only one
- pattern variable <TT>"%u"</TT> which will be replaced by the user's part of a
- JID. For example, <TT>"%u@example.org"</TT>. The default value is <TT>"%u"</TT>.
-<DT CLASS="dt-description"><B><TT>ldap_filter</TT></B><DD CLASS="dd-description">
+ <DT CLASS="dt-description"><B><TT>ldap_uidattr_format</TT></B><DD CLASS="dd-description">Format of
+ the <TT>ldap_uidattr</TT> variable. The format <EM>must</EM> contain one and
+ only one pattern variable <TT>"%u"</TT> which will be replaced by the
+ user's part of a JID. For example, <TT>"%u@example.org"</TT>. The default
+ value is <TT>"%u"</TT>.
+ </DL>
+ <DT CLASS="dt-description"><B><TT>ldap_filter</TT></B><DD CLASS="dd-description">
<A HREF="http://www.faqs.org/rfcs/rfc2254.html">RFC 2254</A> LDAP filter. The
default is <TT>none</TT>. Example:
<TT>"(&(objectClass=shadowAccount)(memberOf=Jabber Users))"</TT>. Please, do
{ldap_password, ""},
%% define the addressbook's base
{ldap_base, "ou=AddressBook,dc=example,dc=org"},
- %% user's part of JID is located in the "mail" attribute
- {ldap_uidattr, "mail"},
- %% common format for our emails
- {ldap_uidattr_format, "%u@mail.example.org"},
+ %% uidattr: user's part of JID is located in the "mail" attribute
+ %% uidattr_format: common format for our emails
+ {ldap_uids, [{"mail", "%u@mail.example.org"}]},
%% We have to define empty filter here, because entries in addressbook does not
%% belong to shadowAccount object class
{ldap_filter, ""},
{ldap_base, "DC=office,DC=org"}. % Search base of LDAP directory
{ldap_rootdn, "CN=Administrator,CN=Users,DC=office,DC=org"}. % LDAP manager
{ldap_password, "*******"}. % Password to LDAP manager
- {ldap_uidattr, "sAMAccountName"}.
+ {ldap_uids, [{"sAMAccountName"}]}.
{ldap_filter, "(memberOf=*)"}.
{mod_vcard_ldap,
{ldap_password, ""},
%% define the addressbook's base
{ldap_base, "ou=AddressBook,dc=example,dc=org"},
- %% user's part of JID is located in the "mail" attribute
- {ldap_uidattr, "mail"},
- %% common format for our emails
- {ldap_uidattr_format, "%u@mail.example.org"},
+ %% uidattr: user's part of JID is located in the "mail" attribute
+ %% uidattr_format: common format for our emails
+ {ldap_uids, [{"mail","%u@mail.example.org"}]},
%% We have to define empty filter here, because entries in addressbook does not
%% belong to shadowAccount object class
{ldap_filter, ""},
{host_config, "example.com", [{auth_method, ldap},
{ldap_servers, ["localhost"]},
- {ldap_uidattr, "uid"},
+ {ldap_uids, [{"uid"}]},
{ldap_rootdn, "dc=localdomain"},
{ldap_rootdn, "dc=example,dc=com"},
{ldap_password, ""}]}.
{host_config, "example.com", [{auth_method, ldap},
{ldap_servers, ["localhost", "otherhost"]},
- {ldap_uidattr, "uid"},
+ {ldap_uids, [{"uid"}]},
{ldap_rootdn, "dc=localdomain"},
{ldap_rootdn, "dc=example,dc=com"},
{ldap_password, ""}]}.
You can authenticate users against an LDAP directory. Available options are:
\begin{description}
-\titem{ldap\_base}\ind{options!ldap\_base}LDAP base directory which stores users
- accounts. This option is required.
-\titem{ldap\_uidattr}\ind{options!ldap\_uidattr}LDAP attribute which holds
- the user's part of a JID. The default value is \term{"uid"}.
-\titem{ldap\_uidattr\_format}\ind{options!ldap\_uidattr\_format}Format of the
- \term{ldap\_uidattr} variable. The format \emph{must} contain one and only one
- pattern variable \term{"\%u"} which will be replaced by the user's part of a
- JID. For example, \term{"\%u@example.org"}. The default value is \term{"\%u"}.
-\titem{ldap\_filter}\ind{options!ldap\_filter}\ind{protocols!RFC 2254: The String Representation of LDAP Search Filters}
+\titem{ldap\_base}\ind{options!ldap\_base}LDAP base directory which stores
+ users accounts. This option is required.
+ \titem{ldap\_uids}\ind{options!ldap\_uids}LDAP attribute which holds a list
+ of attributes to use as alternatives for getting the JID. The value is of
+ the form: \term{[\{ldap\_uidattr\}]} or \term{[\{ldap\_uidattr,
+ ldap\_uidattr\_format\}]}. You can use as many comma separated tuples
+ \term{\{ldap\_uidattr, ldap\_uidattr\_format\}} that is needed. The default
+ value is \term{[\{"uid", "\%u"\}]}. The defaut \term{ldap\_uidattr\_format}
+ is \term{"\%u"}. The values for \term{ldap\_uidattr} and
+ \term{ldap\_uidattr\_format} are described as follow:
+ \begin{description}
+ \titem{ldap\_uidattr}\ind{options!ldap\_uidattr}LDAP attribute which holds
+ the user's part of a JID. The default value is \term{"uid"}.
+ \titem{ldap\_uidattr\_format}\ind{options!ldap\_uidattr\_format}Format of
+ the \term{ldap\_uidattr} variable. The format \emph{must} contain one and
+ only one pattern variable \term{"\%u"} which will be replaced by the
+ user's part of a JID. For example, \term{"\%u@example.org"}. The default
+ value is \term{"\%u"}.
+ \end{description}
+ \titem{ldap\_filter}\ind{options!ldap\_filter}\ind{protocols!RFC 2254: The
+ String Representation of LDAP Search Filters}
\footahref{http://www.faqs.org/rfcs/rfc2254.html}{RFC 2254} LDAP filter. The
default is \term{none}. Example:
\term{"(\&(objectClass=shadowAccount)(memberOf=Jabber Users))"}. Please, do
{ldap_password, ""},
%% define the addressbook's base
{ldap_base, "ou=AddressBook,dc=example,dc=org"},
- %% user's part of JID is located in the "mail" attribute
- {ldap_uidattr, "mail"},
- %% common format for our emails
- {ldap_uidattr_format, "%u@mail.example.org"},
+ %% uidattr: user's part of JID is located in the "mail" attribute
+ %% uidattr_format: common format for our emails
+ {ldap_uids, [{"mail", "%u@mail.example.org"}]},
%% We have to define empty filter here, because entries in addressbook does not
%% belong to shadowAccount object class
{ldap_filter, ""},
{ldap_base, "DC=office,DC=org"}. % Search base of LDAP directory
{ldap_rootdn, "CN=Administrator,CN=Users,DC=office,DC=org"}. % LDAP manager
{ldap_password, "*******"}. % Password to LDAP manager
- {ldap_uidattr, "sAMAccountName"}.
+ {ldap_uids, [{"sAMAccountName"}]}.
{ldap_filter, "(memberOf=*)"}.
{mod_vcard_ldap,
{ldap_password, ""},
%% define the addressbook's base
{ldap_base, "ou=AddressBook,dc=example,dc=org"},
- %% user's part of JID is located in the "mail" attribute
- {ldap_uidattr, "mail"},
- %% common format for our emails
- {ldap_uidattr_format, "%u@mail.example.org"},
+ %% uidattr: user's part of JID is located in the "mail" attribute
+ %% uidattr_format: common format for our emails
+ {ldap_uids, [{"mail","%u@mail.example.org"}]},
%% We have to define empty filter here, because entries in addressbook does not
%% belong to shadowAccount object class
{ldap_filter, ""},
dn,
password,
base,
- uidattr,
- uidattr_format,
+ uids,
ufilter,
sfilter,
dn_filter,
{reply, Reply, State};
handle_call(get_vh_registered_users, _From, State) ->
- UA = State#state.uidattr,
- UAF = State#state.uidattr_format,
+ UIDs = State#state.uids,
Eldap_ID = State#state.eldap_id,
Server = State#state.host,
- SortedDNAttrs = usort_attrs(State#state.dn_filter_attrs),
+ SortedDNAttrs = eldap_utils:usort_attrs(State#state.dn_filter_attrs),
Reply = case eldap_filter:parse(State#state.sfilter) of
{ok, EldapFilter} ->
case eldap:search(Eldap_ID, [{base, State#state.base},
case is_valid_dn(DN, Attrs, State) of
false -> [];
_ ->
- case get_ldap_attr(UA, Attrs) of
+ case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
"" -> [];
- User ->
- case get_user_part(User, UAF) of
+ {User, UIDFormat} ->
+ case eldap_utils:get_user_part(User, UIDFormat) of
{ok, U} ->
case jlib:nodeprep(U) of
error -> [];
{reply, bad_request, State}.
find_user_dn(User, State) ->
- DNAttrs = usort_attrs(State#state.dn_filter_attrs),
+ DNAttrs = eldap_utils:usort_attrs(State#state.dn_filter_attrs),
case eldap_filter:parse(State#state.ufilter, [{"%u", User}]) of
{ok, Filter} ->
case eldap:search(State#state.eldap_id, [{base, State#state.base},
is_valid_dn(DN, Attrs, State) ->
DNAttrs = State#state.dn_filter_attrs,
- UA = State#state.uidattr,
- UAF = State#state.uidattr_format,
- Values = [{"%s", get_ldap_attr(Attr, Attrs), 1} || Attr <- DNAttrs],
- SubstValues = case get_ldap_attr(UA, Attrs) of
+ UIDs = State#state.uids,
+ Values = [{"%s", eldap_utils:get_ldap_attr(Attr, Attrs), 1} || Attr <- DNAttrs],
+ SubstValues = case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
"" -> Values;
- S ->
- case get_user_part(S, UAF) of
+ {S, UAF} ->
+ case eldap_utils:get_user_part(S, UAF) of
{ok, U} -> [{"%u", U} | Values];
_ -> Values
end
%%%----------------------------------------------------------------------
%%% Auxiliary functions
%%%----------------------------------------------------------------------
-get_user_part(String, Pattern) ->
- F = fun(S, P) ->
- First = string:str(P, "%u"),
- TailLength = length(P) - (First+1),
- string:sub_string(S, First, length(S) - TailLength)
- end,
- case catch F(String, Pattern) of
- {'EXIT', _} ->
- {error, badmatch};
- Result ->
- case regexp:sub(Pattern, "%u", Result) of
- {ok, String, _} -> {ok, Result};
- _ -> {error, badmatch}
- end
- end.
-
-case_insensitive_match(X, Y) ->
- X1 = stringprep:tolower(X),
- Y1 = stringprep:tolower(Y),
- if
- X1 == Y1 -> true;
- true -> false
- end.
-
-get_ldap_attr(LDAPAttr, Attributes) ->
- Res = lists:filter(
- fun({Name, _}) ->
- case_insensitive_match(Name, LDAPAttr)
- end, Attributes),
- case Res of
- [{_, [Value|_]}] -> Value;
- _ -> ""
- end.
-
-usort_attrs(Attrs) when is_list(Attrs) ->
- lists:usort(Attrs);
-
-usort_attrs(_) ->
- [].
-
parse_options(Host) ->
Eldap_ID = atom_to_list(gen_mod:get_module_proc(Host, ?MODULE)),
Bind_Eldap_ID = atom_to_list(gen_mod:get_module_proc(Host, bind_ejabberd_auth_ldap)),
undefined -> "";
Pass -> Pass
end,
- UIDAttr = case ejabberd_config:get_local_option({ldap_uidattr, Host}) of
- undefined -> "uid";
- UA -> UA
- end,
- UIDAttrFormat = case ejabberd_config:get_local_option({ldap_uidattr_format, Host}) of
- undefined -> "%u";
- UAF -> UAF
- end,
- SubFilter = "(" ++ UIDAttr ++ "=" ++ UIDAttrFormat ++ ")",
+ UIDs = case ejabberd_config:get_local_option({ldap_uids, Host}) of
+ undefined -> [{"uid", "%u"}];
+ UI -> UI
+ end,
+ SubFilter = lists:flatten(eldap_utils:generate_subfilter(UIDs)),
UserFilter = case ejabberd_config:get_local_option({ldap_filter, Host}) of
undefined -> SubFilter;
"" -> SubFilter;
dn = RootDN,
password = Password,
base = LDAPBase,
- uidattr = UIDAttr,
- uidattr_format = UIDAttrFormat,
+ uids = UIDs,
ufilter = UserFilter,
sfilter = SearchFilter,
dn_filter = DNFilter,
OBJS = \
$(OUTDIR)/eldap.beam \
$(OUTDIR)/ELDAPv3.beam \
- $(OUTDIR)/eldap_filter.beam
+ $(OUTDIR)/eldap_filter.beam \
+ $(OUTDIR)/eldap_utils.beam
all: $(OBJS)
--- /dev/null
+%%%----------------------------------------------------------------------
+%%% File : eldap_utils.erl
+%%% Author : Mickael Remond <mickael.remond@process-one.net>
+%%% Purpose : ejabberd LDAP helper functions
+%%% Created : 12 Oct 2006 by Mickael Remond <mickael.remond@process-one.net>
+%%% Id : $Id: ejabberd_auth_ldap.erl 623 2006-09-23 09:52:53Z mremond $
+%%%----------------------------------------------------------------------
+
+-module(eldap_utils).
+-author('mickael.remond@process-one.net').
+-svn('$Revision: $ ').
+
+-export([generate_subfilter/1,
+ find_ldap_attrs/2,
+ get_ldap_attr/2,
+ usort_attrs/1,
+ get_user_part/2,
+ make_filter/2]).
+
+%% Generate an 'or' LDAP query on one or several attributes
+%% If there is only one attribute
+generate_subfilter([UID]) ->
+ subfilter(UID);
+%% If there is several attributes
+generate_subfilter(UIDs) ->
+ "(|" ++ [subfilter(UID) || UID <- UIDs] ++ ")".
+%% Subfilter for a single attribute
+subfilter({UIDAttr, UIDAttrFormat}) ->
+ "(" ++ UIDAttr ++ "=" ++ UIDAttrFormat ++ ")";
+%% The default UiDAttrFormat is %u
+subfilter({UIDAttr}) ->
+ "(" ++ UIDAttr ++ "=" ++ "%u)".
+
+%% Not tail-recursive, but it is not very terribly.
+%% It stops finding on the first not empty value.
+find_ldap_attrs([{Attr, Format} | Rest], Attributes) ->
+ case get_ldap_attr(Attr, Attributes) of
+ Value when is_list(Value), Value /= "" ->
+ {Value, Format};
+ _ ->
+ find_ldap_attrs(Rest, Attributes)
+ end;
+find_ldap_attrs([], _) ->
+ "".
+
+get_ldap_attr(LDAPAttr, Attributes) ->
+ Res = lists:filter(
+ fun({Name, _}) ->
+ case_insensitive_match(Name, LDAPAttr)
+ end, Attributes),
+ case Res of
+ [{_, [Value|_]}] -> Value;
+ _ -> ""
+ end.
+
+
+usort_attrs(Attrs) when is_list(Attrs) ->
+ lists:usort(Attrs);
+usort_attrs(_) ->
+ [].
+
+get_user_part(String, Pattern) ->
+ F = fun(S, P) ->
+ First = string:str(P, "%u"),
+ TailLength = length(P) - (First+1),
+ string:sub_string(S, First, length(S) - TailLength)
+ end,
+ case catch F(String, Pattern) of
+ {'EXIT', _} ->
+ {error, badmatch};
+ Result ->
+ case regexp:sub(Pattern, "%u", Result) of
+ {ok, String, _} -> {ok, Result};
+ _ -> {error, badmatch}
+ end
+ end.
+
+make_filter(Data, UIDs) ->
+ NewUIDs = [{U, eldap_filter:do_sub(UF, [{"%u", "*%u*", 1}])} || {U, UF} <- UIDs],
+ Filter = lists:flatmap(
+ fun({Name, [Value | _]}) ->
+ case Name of
+ "%u" when Value /= "" ->
+ case eldap_filter:parse(
+ lists:flatten(generate_subfilter(NewUIDs)),
+ [{"%u", Value}]) of
+ {ok, F} -> [F];
+ _ -> []
+ end;
+ _ when Value /= "" ->
+ [eldap:substrings(Name, [{any, Value}])];
+ _ ->
+ []
+ end
+ end, Data),
+ case Filter of
+ [F] ->
+ F;
+ _ ->
+ eldap:'and'(Filter)
+ end.
+
+case_insensitive_match(X, Y) ->
+ X1 = stringprep:tolower(X),
+ Y1 = stringprep:tolower(Y),
+ if
+ X1 == Y1 -> true;
+ true -> false
+ end.
+
dn,
base,
password,
- uid,
- uid_format,
+ uids,
vcard_map,
vcard_map_attrs,
user_filter,
Base = State#state.base,
SearchFilter = State#state.search_filter,
Eldap_ID = State#state.eldap_id,
- UA = State#state.uid,
- UAF = State#state.uid_format,
+ UIDs = State#state.uids,
ReportedAttrs = State#state.search_reported_attrs,
- Filter = eldap:'and'([SearchFilter, make_filter(Data, UA, UAF)]),
+ Filter = eldap:'and'([SearchFilter, eldap_utils:make_filter(Data, UIDs)]),
case eldap:search(Eldap_ID, [{base, Base},
{filter, Filter},
{attributes, ReportedAttrs}]) of
LServer = State#state.serverhost,
SearchReported = State#state.search_reported,
VCardMap = State#state.vcard_map,
- UIDAttr = State#state.uid,
- UIDAttrFormat = State#state.uid_format,
+ UIDs = State#state.uids,
Attributes = lists:map(
fun(E) ->
#eldap_entry{attributes = Attrs} = E,
end, Entries),
lists:flatmap(
fun(Attrs) ->
- U = get_ldap_attr(UIDAttr, Attrs),
- case get_user_part(U, UIDAttrFormat) of
- {ok, Username} ->
- case ejabberd_auth:is_user_exists(Username, LServer) of
- true ->
- RFields = lists:map(
+ case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
+ {U, UIDAttrFormat} ->
+ case eldap_utils:get_user_part(U, UIDAttrFormat) of
+ {ok, Username} ->
+ case ejabberd_auth:is_user_exists(Username, LServer) of
+ true ->
+ RFields = lists:map(
fun({_, VCardName}) ->
{VCardName,
map_vcard_attr(
VCardMap,
{Username, ?MYNAME})}
end, SearchReported),
- Result = [?FIELD("jid", Username ++ "@" ++ LServer)] ++
- [?FIELD(Name, Value) || {Name, Value} <- RFields],
- [{xmlelement, "item", [], Result}];
- _ ->
- []
- end;
- _ ->
+ Result = [?FIELD("jid", Username ++ "@" ++ LServer)] ++
+ [?FIELD(Name, Value) || {Name, Value} <- RFields],
+ [{xmlelement, "item", [], Result}];
+ _ ->
+ []
+ end;
+ _ ->
+ []
+ end;
+ "" ->
[]
- end
+ end
end, Attributes).
-make_filter(Data, UAttr, UAttrFormat) ->
- Filter = lists:flatmap(
- fun({Name, [Value | _]}) ->
- case Name of
- "%u" when Value /= "" ->
- {ok, UAF, _} = regexp:sub(UAttrFormat, "%u", "*%u*"),
- case eldap_filter:parse(
- "("++UAttr++"="++UAF++")", [{"%u", Value}]) of
- {ok, F} -> [F];
- _ -> []
- end;
- _ when Value /= "" ->
- [eldap:substrings(Name, [{any, Value}])];
- _ ->
- []
- end
- end, Data),
- case Filter of
- [F] ->
- F;
- _ ->
- eldap:'and'(Filter)
- end.
-
remove_user(_User) ->
true.
%%% Auxiliary functions.
%%%-----------------------
-get_user_part(String, Pattern) ->
- F = fun(S, P) ->
- First = string:str(P, "%u"),
- TailLength = length(P) - (First+1),
- string:sub_string(S, First, length(S) - TailLength)
- end,
- case catch F(String, Pattern) of
- {'EXIT', _} ->
- {error, badmatch};
- Result ->
- case regexp:sub(Pattern, "%u", Result) of
- {ok, String, _} -> {ok, Result};
- _ -> {error, badmatch}
- end
- end.
-
-case_insensitive_match(X, Y) ->
- X1 = stringprep:tolower(X),
- Y1 = stringprep:tolower(Y),
- if
- X1 == Y1 -> true;
- true -> false
- end.
-
map_vcard_attr(VCardName, Attributes, Pattern, UD) ->
Res = lists:filter(
fun({Name, _, _}) ->
- case_insensitive_match(Name, VCardName)
+ eldap_utils:case_insensitive_match(Name, VCardName)
end, Pattern),
case Res of
[{_, Str, Attrs}] ->
process_pattern(Str, UD,
- [get_ldap_attr(X, Attributes) || X<-Attrs]);
+ [eldap_utils:get_ldap_attr(X, Attributes) || X<-Attrs]);
_ -> ""
end.
eldap_filter:do_sub(Str,
[{"%s", V, 1} || V <- AttrValues] ++ [{"%u", User},{"%d", Domain}]).
-get_ldap_attr(LDAPAttr, Attributes) ->
- Res = lists:filter(
- fun({Name, _}) ->
- case_insensitive_match(Name, LDAPAttr)
- end, Attributes),
- case Res of
- [{_, [Value|_]}] -> Value;
- _ -> ""
- end.
-
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
find_xdata_el1(SubEls).
ejabberd_config:get_local_option({ldap_base, Host});
B -> B
end,
- UIDAttr = case gen_mod:get_opt(ldap_uidattr, Opts, undefined) of
- undefined ->
- case ejabberd_config:get_local_option({ldap_uidattr, Host}) of
- undefined -> "uid";
- UA -> UA
- end;
- UA -> UA
- end,
- UIDAttrFormat = case gen_mod:get_opt(ldap_uidattr_format, Opts, undefined) of
- undefined ->
- case ejabberd_config:get_local_option({ldap_uidattr_format, Host}) of
- undefined -> "%u";
- UAF -> UAF
- end;
- UAF -> UAF
- end,
+ UIDs = case gen_mod:get_opt(ldap_uids, Opts, undefined) of
+ undefined ->
+ case ejabberd_config:get_local_option({ldap_uids, Host}) of
+ undefined -> [{"uid", "%u"}];
+ UI -> UI
+ end;
+ UI -> UI
+ end,
RootDN = case gen_mod:get_opt(ldap_rootdn, Opts, undefined) of
undefined ->
case ejabberd_config:get_local_option({ldap_rootdn, Host}) of
end;
Pass -> Pass
end,
- SubFilter = "("++UIDAttr++"="++UIDAttrFormat++")",
+ SubFilter = lists:flatten(eldap_utils:generate_subfilter(UIDs)),
UserFilter = case gen_mod:get_opt(ldap_filter, Opts, undefined) of
undefined ->
case ejabberd_config:get_local_option({ldap_filter, Host}) of
%% In search requests we need to fetch only attributes defined
%% in vcard-map and search-reported. In some cases,
%% this will essentially reduce network traffic from an LDAP server.
+ UIDAttrs = [UAttr || {UAttr, _} <- UIDs],
VCardMapAttrs = lists:usort(
- lists:append([A || {_, _, A} <- VCardMap]) ++ [UIDAttr]),
+ lists:append([A || {_, _, A} <- VCardMap]) ++ UIDAttrs),
SearchReportedAttrs =
lists:usort(lists:flatmap(
fun({_, N}) ->
{value, {_, _, L}} -> L;
_ -> []
end
- end, SearchReported) ++ [UIDAttr]),
+ end, SearchReported) ++ UIDAttrs),
#state{serverhost = Host,
myhost = MyHost,
eldap_id = Eldap_ID,
dn = RootDN,
base = LDAPBase,
password = Password,
- uid = UIDAttr,
- uid_format = UIDAttrFormat,
+ uids = UIDs,
vcard_map = VCardMap,
vcard_map_attrs = VCardMapAttrs,
user_filter = UserFilter,