diff --git a/ChangeLog b/ChangeLog index ff1a57aa4..60f12f98f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2006-10-17 Mickael Remond + + * 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 * src/mod_privacy_odbc.erl: Privacy rules support using odbc diff --git a/doc/guide.html b/doc/guide.html index 3c03f28ce..df3896689 100644 --- a/doc/guide.html +++ b/doc/guide.html @@ -521,7 +521,7 @@ Domain example.net is using the internal authentication method while {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, ""}]}. @@ -534,7 +534,7 @@ Domain example.net is using the internal authentication method while {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, ""}]}. @@ -1372,15 +1372,26 @@ and SASL authentication.
You can authenticate users against an LDAP directory. Available options are:
-ldap_base
LDAP base directory which stores users - accounts. This option is required. -
ldap_uidattr
LDAP attribute which holds +ldap_base
LDAP base directory which stores + users accounts. This option is required. +
ldap_uids
LDAP attribute which holds a list + of attributes to use as alternatives for getting the JID. The value is of + the form: [{ldap_uidattr}] or [{ldap_uidattr, + ldap_uidattr_format}]. You can use as many comma separated tuples + {ldap_uidattr, ldap_uidattr_format} that is needed. The default + value is [{"uid", "%u"}]. The defaut ldap_uidattr_format + is "%u". The values for ldap_uidattr and + ldap_uidattr_format are described as follow: +
+ ldap_uidattr
LDAP attribute which holds the user's part of a JID. The default value is "uid". -
ldap_uidattr_format
Format of the - ldap_uidattr variable. The format must contain one and only one - pattern variable "%u" which will be replaced by the user's part of a - JID. For example, "%u@example.org". The default value is "%u". -
ldap_filter
+
ldap_uidattr_format
Format of + the ldap_uidattr variable. The format must contain one and + only one pattern variable "%u" which will be replaced by the + user's part of a JID. For example, "%u@example.org". The default + value is "%u". +
+
ldap_filter
RFC 2254 LDAP filter. The default is none. Example: "(&(objectClass=shadowAccount)(memberOf=Jabber Users))". Please, do @@ -1431,10 +1442,9 @@ Also we want users to search each other. Let's see how we can set it up: {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, ""}, @@ -1480,7 +1490,7 @@ configuration is showed below: {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, @@ -2881,10 +2891,9 @@ Also we want users to search each other. Let's see how we can set it up: {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, ""}, diff --git a/doc/guide.tex b/doc/guide.tex index 174dc95a6..3207b8bee 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -378,7 +378,7 @@ Examples: {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, ""}]}. @@ -392,7 +392,7 @@ Examples: {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, ""}]}. @@ -1230,15 +1230,27 @@ and SASL authentication. 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 @@ -1289,10 +1301,9 @@ Also we want users to search each other. Let's see how we can set it up: {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, ""}, @@ -1339,7 +1350,7 @@ configuration is showed below: {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, @@ -2599,10 +2610,9 @@ Also we want users to search each other. Let's see how we can set it up: {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, ""}, diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl index d152d0895..19b0c9e9d 100644 --- a/src/ejabberd_auth_ldap.erl +++ b/src/ejabberd_auth_ldap.erl @@ -50,8 +50,7 @@ dn, password, base, - uidattr, - uidattr_format, + uids, ufilter, sfilter, dn_filter, @@ -188,11 +187,10 @@ handle_call({check_pass, User, Password}, _From, State) -> {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}, @@ -205,10 +203,10 @@ handle_call(get_vh_registered_users, _From, State) -> 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 -> []; @@ -241,7 +239,7 @@ handle_call(_Request, _From, State) -> {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}, @@ -262,13 +260,12 @@ is_valid_dn(DN, _, #state{dn_filter = undefined}) -> 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 @@ -291,46 +288,6 @@ is_valid_dn(DN, Attrs, State) -> %%%---------------------------------------------------------------------- %%% 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)), @@ -347,15 +304,11 @@ parse_options(Host) -> 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; @@ -376,8 +329,7 @@ parse_options(Host) -> dn = RootDN, password = Password, base = LDAPBase, - uidattr = UIDAttr, - uidattr_format = UIDAttrFormat, + uids = UIDs, ufilter = UserFilter, sfilter = SearchFilter, dn_filter = DNFilter, diff --git a/src/eldap/Makefile.in b/src/eldap/Makefile.in index 72e7b4632..de7933831 100644 --- a/src/eldap/Makefile.in +++ b/src/eldap/Makefile.in @@ -13,7 +13,8 @@ EFLAGS = -I .. -pz .. OBJS = \ $(OUTDIR)/eldap.beam \ $(OUTDIR)/ELDAPv3.beam \ - $(OUTDIR)/eldap_filter.beam + $(OUTDIR)/eldap_filter.beam \ + $(OUTDIR)/eldap_utils.beam all: $(OBJS) diff --git a/src/eldap/eldap_utils.erl b/src/eldap/eldap_utils.erl new file mode 100644 index 000000000..b4eb67f86 --- /dev/null +++ b/src/eldap/eldap_utils.erl @@ -0,0 +1,110 @@ +%%%---------------------------------------------------------------------- +%%% File : eldap_utils.erl +%%% Author : Mickael Remond +%%% Purpose : ejabberd LDAP helper functions +%%% Created : 12 Oct 2006 by Mickael Remond +%%% 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. + diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl index 0921ce92f..f0ec35462 100644 --- a/src/mod_vcard_ldap.erl +++ b/src/mod_vcard_ldap.erl @@ -46,8 +46,7 @@ dn, base, password, - uid, - uid_format, + uids, vcard_map, vcard_map_attrs, user_filter, @@ -552,10 +551,9 @@ search(State, Data) -> 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 @@ -569,8 +567,7 @@ search_items(Entries, State) -> 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, @@ -578,12 +575,13 @@ search_items(Entries, State) -> 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( @@ -592,41 +590,20 @@ search_items(Entries, State) -> 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. @@ -634,39 +611,15 @@ remove_user(_User) -> %%% 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. @@ -674,16 +627,6 @@ process_pattern(Str, {User, Domain}, AttrValues) -> 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). @@ -721,22 +664,14 @@ parse_options(Host, Opts) -> 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 @@ -753,7 +688,7 @@ parse_options(Host, Opts) -> 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 @@ -772,8 +707,9 @@ parse_options(Host, Opts) -> %% 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}) -> @@ -781,7 +717,7 @@ parse_options(Host, Opts) -> {value, {_, _, L}} -> L; _ -> [] end - end, SearchReported) ++ [UIDAttr]), + end, SearchReported) ++ UIDAttrs), #state{serverhost = Host, myhost = MyHost, eldap_id = Eldap_ID, @@ -791,8 +727,7 @@ parse_options(Host, Opts) -> dn = RootDN, base = LDAPBase, password = Password, - uid = UIDAttr, - uid_format = UIDAttrFormat, + uids = UIDs, vcard_map = VCardMap, vcard_map_attrs = VCardMapAttrs, user_filter = UserFilter,