* 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. SVN Revision: 661
This commit is contained in:
parent
95e2726f71
commit
d5792ed75e
|
@ -1,3 +1,12 @@
|
||||||
|
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>
|
2006-10-09 Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
|
||||||
* src/mod_privacy_odbc.erl: Privacy rules support using odbc
|
* src/mod_privacy_odbc.erl: Privacy rules support using odbc
|
||||||
|
|
|
@ -521,7 +521,7 @@ Domain <TT>example.net</TT> is using the internal authentication method while
|
||||||
|
|
||||||
{host_config, "example.com", [{auth_method, ldap},
|
{host_config, "example.com", [{auth_method, ldap},
|
||||||
{ldap_servers, ["localhost"]},
|
{ldap_servers, ["localhost"]},
|
||||||
{ldap_uidattr, "uid"},
|
{ldap_uids, [{"uid"}]},
|
||||||
{ldap_rootdn, "dc=localdomain"},
|
{ldap_rootdn, "dc=localdomain"},
|
||||||
{ldap_rootdn, "dc=example,dc=com"},
|
{ldap_rootdn, "dc=example,dc=com"},
|
||||||
{ldap_password, ""}]}.
|
{ldap_password, ""}]}.
|
||||||
|
@ -534,7 +534,7 @@ Domain <TT>example.net</TT> is using the internal authentication method while
|
||||||
|
|
||||||
{host_config, "example.com", [{auth_method, ldap},
|
{host_config, "example.com", [{auth_method, ldap},
|
||||||
{ldap_servers, ["localhost", "otherhost"]},
|
{ldap_servers, ["localhost", "otherhost"]},
|
||||||
{ldap_uidattr, "uid"},
|
{ldap_uids, [{"uid"}]},
|
||||||
{ldap_rootdn, "dc=localdomain"},
|
{ldap_rootdn, "dc=localdomain"},
|
||||||
{ldap_rootdn, "dc=example,dc=com"},
|
{ldap_rootdn, "dc=example,dc=com"},
|
||||||
{ldap_password, ""}]}.
|
{ldap_password, ""}]}.
|
||||||
|
@ -1372,15 +1372,26 @@ and SASL authentication.<BR>
|
||||||
<A NAME="sec:ldapauth"></A>
|
<A NAME="sec:ldapauth"></A>
|
||||||
You can authenticate users against an LDAP directory. Available options are:
|
You can authenticate users against an LDAP directory. Available options are:
|
||||||
<DL CLASS="description" COMPACT=compact><DT CLASS="dt-description">
|
<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
|
<B><TT>ldap_base</TT></B><DD CLASS="dd-description">LDAP base directory which stores
|
||||||
accounts. This option is required.
|
users accounts. This option is required.
|
||||||
<DT CLASS="dt-description"><B><TT>ldap_uidattr</TT></B><DD CLASS="dd-description">LDAP attribute which holds
|
<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>.
|
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
|
<DT CLASS="dt-description"><B><TT>ldap_uidattr_format</TT></B><DD CLASS="dd-description">Format of
|
||||||
<TT>ldap_uidattr</TT> variable. The format <EM>must</EM> contain one and only one
|
the <TT>ldap_uidattr</TT> variable. The format <EM>must</EM> contain one and
|
||||||
pattern variable <TT>"%u"</TT> which will be replaced by the user's part of a
|
only one pattern variable <TT>"%u"</TT> which will be replaced by the
|
||||||
JID. For example, <TT>"%u@example.org"</TT>. The default value is <TT>"%u"</TT>.
|
user's part of a JID. For example, <TT>"%u@example.org"</TT>. The default
|
||||||
<DT CLASS="dt-description"><B><TT>ldap_filter</TT></B><DD CLASS="dd-description">
|
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
|
<A HREF="http://www.faqs.org/rfcs/rfc2254.html">RFC 2254</A> LDAP filter. The
|
||||||
default is <TT>none</TT>. Example:
|
default is <TT>none</TT>. Example:
|
||||||
<TT>"(&(objectClass=shadowAccount)(memberOf=Jabber Users))"</TT>. Please, do
|
<TT>"(&(objectClass=shadowAccount)(memberOf=Jabber Users))"</TT>. 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, ""},
|
{ldap_password, ""},
|
||||||
%% define the addressbook's base
|
%% define the addressbook's base
|
||||||
{ldap_base, "ou=AddressBook,dc=example,dc=org"},
|
{ldap_base, "ou=AddressBook,dc=example,dc=org"},
|
||||||
%% user's part of JID is located in the "mail" attribute
|
%% uidattr: user's part of JID is located in the "mail" attribute
|
||||||
{ldap_uidattr, "mail"},
|
%% uidattr_format: common format for our emails
|
||||||
%% common format for our emails
|
{ldap_uids, [{"mail", "%u@mail.example.org"}]},
|
||||||
{ldap_uidattr_format, "%u@mail.example.org"},
|
|
||||||
%% We have to define empty filter here, because entries in addressbook does not
|
%% We have to define empty filter here, because entries in addressbook does not
|
||||||
%% belong to shadowAccount object class
|
%% belong to shadowAccount object class
|
||||||
{ldap_filter, ""},
|
{ldap_filter, ""},
|
||||||
|
@ -1480,7 +1490,7 @@ configuration is showed below:
|
||||||
{ldap_base, "DC=office,DC=org"}. % Search base of LDAP directory
|
{ldap_base, "DC=office,DC=org"}. % Search base of LDAP directory
|
||||||
{ldap_rootdn, "CN=Administrator,CN=Users,DC=office,DC=org"}. % LDAP manager
|
{ldap_rootdn, "CN=Administrator,CN=Users,DC=office,DC=org"}. % LDAP manager
|
||||||
{ldap_password, "*******"}. % Password to LDAP manager
|
{ldap_password, "*******"}. % Password to LDAP manager
|
||||||
{ldap_uidattr, "sAMAccountName"}.
|
{ldap_uids, [{"sAMAccountName"}]}.
|
||||||
{ldap_filter, "(memberOf=*)"}.
|
{ldap_filter, "(memberOf=*)"}.
|
||||||
|
|
||||||
{mod_vcard_ldap,
|
{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, ""},
|
{ldap_password, ""},
|
||||||
%% define the addressbook's base
|
%% define the addressbook's base
|
||||||
{ldap_base, "ou=AddressBook,dc=example,dc=org"},
|
{ldap_base, "ou=AddressBook,dc=example,dc=org"},
|
||||||
%% user's part of JID is located in the "mail" attribute
|
%% uidattr: user's part of JID is located in the "mail" attribute
|
||||||
{ldap_uidattr, "mail"},
|
%% uidattr_format: common format for our emails
|
||||||
%% common format for our emails
|
{ldap_uids, [{"mail","%u@mail.example.org"}]},
|
||||||
{ldap_uidattr_format, "%u@mail.example.org"},
|
|
||||||
%% We have to define empty filter here, because entries in addressbook does not
|
%% We have to define empty filter here, because entries in addressbook does not
|
||||||
%% belong to shadowAccount object class
|
%% belong to shadowAccount object class
|
||||||
{ldap_filter, ""},
|
{ldap_filter, ""},
|
||||||
|
|
|
@ -378,7 +378,7 @@ Examples:
|
||||||
|
|
||||||
{host_config, "example.com", [{auth_method, ldap},
|
{host_config, "example.com", [{auth_method, ldap},
|
||||||
{ldap_servers, ["localhost"]},
|
{ldap_servers, ["localhost"]},
|
||||||
{ldap_uidattr, "uid"},
|
{ldap_uids, [{"uid"}]},
|
||||||
{ldap_rootdn, "dc=localdomain"},
|
{ldap_rootdn, "dc=localdomain"},
|
||||||
{ldap_rootdn, "dc=example,dc=com"},
|
{ldap_rootdn, "dc=example,dc=com"},
|
||||||
{ldap_password, ""}]}.
|
{ldap_password, ""}]}.
|
||||||
|
@ -392,7 +392,7 @@ Examples:
|
||||||
|
|
||||||
{host_config, "example.com", [{auth_method, ldap},
|
{host_config, "example.com", [{auth_method, ldap},
|
||||||
{ldap_servers, ["localhost", "otherhost"]},
|
{ldap_servers, ["localhost", "otherhost"]},
|
||||||
{ldap_uidattr, "uid"},
|
{ldap_uids, [{"uid"}]},
|
||||||
{ldap_rootdn, "dc=localdomain"},
|
{ldap_rootdn, "dc=localdomain"},
|
||||||
{ldap_rootdn, "dc=example,dc=com"},
|
{ldap_rootdn, "dc=example,dc=com"},
|
||||||
{ldap_password, ""}]}.
|
{ldap_password, ""}]}.
|
||||||
|
@ -1230,15 +1230,27 @@ and SASL authentication.
|
||||||
You can authenticate users against an LDAP directory. Available options are:
|
You can authenticate users against an LDAP directory. Available options are:
|
||||||
|
|
||||||
\begin{description}
|
\begin{description}
|
||||||
\titem{ldap\_base}\ind{options!ldap\_base}LDAP base directory which stores users
|
\titem{ldap\_base}\ind{options!ldap\_base}LDAP base directory which stores
|
||||||
accounts. This option is required.
|
users accounts. This option is required.
|
||||||
\titem{ldap\_uidattr}\ind{options!ldap\_uidattr}LDAP attribute which holds
|
\titem{ldap\_uids}\ind{options!ldap\_uids}LDAP attribute which holds a list
|
||||||
the user's part of a JID. The default value is \term{"uid"}.
|
of attributes to use as alternatives for getting the JID. The value is of
|
||||||
\titem{ldap\_uidattr\_format}\ind{options!ldap\_uidattr\_format}Format of the
|
the form: \term{[\{ldap\_uidattr\}]} or \term{[\{ldap\_uidattr,
|
||||||
\term{ldap\_uidattr} variable. The format \emph{must} contain one and only one
|
ldap\_uidattr\_format\}]}. You can use as many comma separated tuples
|
||||||
pattern variable \term{"\%u"} which will be replaced by the user's part of a
|
\term{\{ldap\_uidattr, ldap\_uidattr\_format\}} that is needed. The default
|
||||||
JID. For example, \term{"\%u@example.org"}. The default value is \term{"\%u"}.
|
value is \term{[\{"uid", "\%u"\}]}. The defaut \term{ldap\_uidattr\_format}
|
||||||
\titem{ldap\_filter}\ind{options!ldap\_filter}\ind{protocols!RFC 2254: The String Representation of LDAP Search Filters}
|
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
|
\footahref{http://www.faqs.org/rfcs/rfc2254.html}{RFC 2254} LDAP filter. The
|
||||||
default is \term{none}. Example:
|
default is \term{none}. Example:
|
||||||
\term{"(\&(objectClass=shadowAccount)(memberOf=Jabber Users))"}. Please, do
|
\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, ""},
|
{ldap_password, ""},
|
||||||
%% define the addressbook's base
|
%% define the addressbook's base
|
||||||
{ldap_base, "ou=AddressBook,dc=example,dc=org"},
|
{ldap_base, "ou=AddressBook,dc=example,dc=org"},
|
||||||
%% user's part of JID is located in the "mail" attribute
|
%% uidattr: user's part of JID is located in the "mail" attribute
|
||||||
{ldap_uidattr, "mail"},
|
%% uidattr_format: common format for our emails
|
||||||
%% common format for our emails
|
{ldap_uids, [{"mail", "%u@mail.example.org"}]},
|
||||||
{ldap_uidattr_format, "%u@mail.example.org"},
|
|
||||||
%% We have to define empty filter here, because entries in addressbook does not
|
%% We have to define empty filter here, because entries in addressbook does not
|
||||||
%% belong to shadowAccount object class
|
%% belong to shadowAccount object class
|
||||||
{ldap_filter, ""},
|
{ldap_filter, ""},
|
||||||
|
@ -1339,7 +1350,7 @@ configuration is showed below:
|
||||||
{ldap_base, "DC=office,DC=org"}. % Search base of LDAP directory
|
{ldap_base, "DC=office,DC=org"}. % Search base of LDAP directory
|
||||||
{ldap_rootdn, "CN=Administrator,CN=Users,DC=office,DC=org"}. % LDAP manager
|
{ldap_rootdn, "CN=Administrator,CN=Users,DC=office,DC=org"}. % LDAP manager
|
||||||
{ldap_password, "*******"}. % Password to LDAP manager
|
{ldap_password, "*******"}. % Password to LDAP manager
|
||||||
{ldap_uidattr, "sAMAccountName"}.
|
{ldap_uids, [{"sAMAccountName"}]}.
|
||||||
{ldap_filter, "(memberOf=*)"}.
|
{ldap_filter, "(memberOf=*)"}.
|
||||||
|
|
||||||
{mod_vcard_ldap,
|
{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, ""},
|
{ldap_password, ""},
|
||||||
%% define the addressbook's base
|
%% define the addressbook's base
|
||||||
{ldap_base, "ou=AddressBook,dc=example,dc=org"},
|
{ldap_base, "ou=AddressBook,dc=example,dc=org"},
|
||||||
%% user's part of JID is located in the "mail" attribute
|
%% uidattr: user's part of JID is located in the "mail" attribute
|
||||||
{ldap_uidattr, "mail"},
|
%% uidattr_format: common format for our emails
|
||||||
%% common format for our emails
|
{ldap_uids, [{"mail","%u@mail.example.org"}]},
|
||||||
{ldap_uidattr_format, "%u@mail.example.org"},
|
|
||||||
%% We have to define empty filter here, because entries in addressbook does not
|
%% We have to define empty filter here, because entries in addressbook does not
|
||||||
%% belong to shadowAccount object class
|
%% belong to shadowAccount object class
|
||||||
{ldap_filter, ""},
|
{ldap_filter, ""},
|
||||||
|
|
|
@ -50,8 +50,7 @@
|
||||||
dn,
|
dn,
|
||||||
password,
|
password,
|
||||||
base,
|
base,
|
||||||
uidattr,
|
uids,
|
||||||
uidattr_format,
|
|
||||||
ufilter,
|
ufilter,
|
||||||
sfilter,
|
sfilter,
|
||||||
dn_filter,
|
dn_filter,
|
||||||
|
@ -188,11 +187,10 @@ handle_call({check_pass, User, Password}, _From, State) ->
|
||||||
{reply, Reply, State};
|
{reply, Reply, State};
|
||||||
|
|
||||||
handle_call(get_vh_registered_users, _From, State) ->
|
handle_call(get_vh_registered_users, _From, State) ->
|
||||||
UA = State#state.uidattr,
|
UIDs = State#state.uids,
|
||||||
UAF = State#state.uidattr_format,
|
|
||||||
Eldap_ID = State#state.eldap_id,
|
Eldap_ID = State#state.eldap_id,
|
||||||
Server = State#state.host,
|
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
|
Reply = case eldap_filter:parse(State#state.sfilter) of
|
||||||
{ok, EldapFilter} ->
|
{ok, EldapFilter} ->
|
||||||
case eldap:search(Eldap_ID, [{base, State#state.base},
|
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
|
case is_valid_dn(DN, Attrs, State) of
|
||||||
false -> [];
|
false -> [];
|
||||||
_ ->
|
_ ->
|
||||||
case get_ldap_attr(UA, Attrs) of
|
case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
|
||||||
"" -> [];
|
"" -> [];
|
||||||
User ->
|
{User, UIDFormat} ->
|
||||||
case get_user_part(User, UAF) of
|
case eldap_utils:get_user_part(User, UIDFormat) of
|
||||||
{ok, U} ->
|
{ok, U} ->
|
||||||
case jlib:nodeprep(U) of
|
case jlib:nodeprep(U) of
|
||||||
error -> [];
|
error -> [];
|
||||||
|
@ -241,7 +239,7 @@ handle_call(_Request, _From, State) ->
|
||||||
{reply, bad_request, State}.
|
{reply, bad_request, State}.
|
||||||
|
|
||||||
find_user_dn(User, 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
|
case eldap_filter:parse(State#state.ufilter, [{"%u", User}]) of
|
||||||
{ok, Filter} ->
|
{ok, Filter} ->
|
||||||
case eldap:search(State#state.eldap_id, [{base, State#state.base},
|
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) ->
|
is_valid_dn(DN, Attrs, State) ->
|
||||||
DNAttrs = State#state.dn_filter_attrs,
|
DNAttrs = State#state.dn_filter_attrs,
|
||||||
UA = State#state.uidattr,
|
UIDs = State#state.uids,
|
||||||
UAF = State#state.uidattr_format,
|
Values = [{"%s", eldap_utils:get_ldap_attr(Attr, Attrs), 1} || Attr <- DNAttrs],
|
||||||
Values = [{"%s", get_ldap_attr(Attr, Attrs), 1} || Attr <- DNAttrs],
|
SubstValues = case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
|
||||||
SubstValues = case get_ldap_attr(UA, Attrs) of
|
|
||||||
"" -> Values;
|
"" -> Values;
|
||||||
S ->
|
{S, UAF} ->
|
||||||
case get_user_part(S, UAF) of
|
case eldap_utils:get_user_part(S, UAF) of
|
||||||
{ok, U} -> [{"%u", U} | Values];
|
{ok, U} -> [{"%u", U} | Values];
|
||||||
_ -> Values
|
_ -> Values
|
||||||
end
|
end
|
||||||
|
@ -291,46 +288,6 @@ is_valid_dn(DN, Attrs, State) ->
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Auxiliary functions
|
%%% 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) ->
|
parse_options(Host) ->
|
||||||
Eldap_ID = atom_to_list(gen_mod:get_module_proc(Host, ?MODULE)),
|
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)),
|
Bind_Eldap_ID = atom_to_list(gen_mod:get_module_proc(Host, bind_ejabberd_auth_ldap)),
|
||||||
|
@ -347,15 +304,11 @@ parse_options(Host) ->
|
||||||
undefined -> "";
|
undefined -> "";
|
||||||
Pass -> Pass
|
Pass -> Pass
|
||||||
end,
|
end,
|
||||||
UIDAttr = case ejabberd_config:get_local_option({ldap_uidattr, Host}) of
|
UIDs = case ejabberd_config:get_local_option({ldap_uids, Host}) of
|
||||||
undefined -> "uid";
|
undefined -> [{"uid", "%u"}];
|
||||||
UA -> UA
|
UI -> UI
|
||||||
end,
|
end,
|
||||||
UIDAttrFormat = case ejabberd_config:get_local_option({ldap_uidattr_format, Host}) of
|
SubFilter = lists:flatten(eldap_utils:generate_subfilter(UIDs)),
|
||||||
undefined -> "%u";
|
|
||||||
UAF -> UAF
|
|
||||||
end,
|
|
||||||
SubFilter = "(" ++ UIDAttr ++ "=" ++ UIDAttrFormat ++ ")",
|
|
||||||
UserFilter = case ejabberd_config:get_local_option({ldap_filter, Host}) of
|
UserFilter = case ejabberd_config:get_local_option({ldap_filter, Host}) of
|
||||||
undefined -> SubFilter;
|
undefined -> SubFilter;
|
||||||
"" -> SubFilter;
|
"" -> SubFilter;
|
||||||
|
@ -376,8 +329,7 @@ parse_options(Host) ->
|
||||||
dn = RootDN,
|
dn = RootDN,
|
||||||
password = Password,
|
password = Password,
|
||||||
base = LDAPBase,
|
base = LDAPBase,
|
||||||
uidattr = UIDAttr,
|
uids = UIDs,
|
||||||
uidattr_format = UIDAttrFormat,
|
|
||||||
ufilter = UserFilter,
|
ufilter = UserFilter,
|
||||||
sfilter = SearchFilter,
|
sfilter = SearchFilter,
|
||||||
dn_filter = DNFilter,
|
dn_filter = DNFilter,
|
||||||
|
|
|
@ -13,7 +13,8 @@ EFLAGS = -I .. -pz ..
|
||||||
OBJS = \
|
OBJS = \
|
||||||
$(OUTDIR)/eldap.beam \
|
$(OUTDIR)/eldap.beam \
|
||||||
$(OUTDIR)/ELDAPv3.beam \
|
$(OUTDIR)/ELDAPv3.beam \
|
||||||
$(OUTDIR)/eldap_filter.beam
|
$(OUTDIR)/eldap_filter.beam \
|
||||||
|
$(OUTDIR)/eldap_utils.beam
|
||||||
|
|
||||||
all: $(OBJS)
|
all: $(OBJS)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% 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.
|
||||||
|
|
|
@ -46,8 +46,7 @@
|
||||||
dn,
|
dn,
|
||||||
base,
|
base,
|
||||||
password,
|
password,
|
||||||
uid,
|
uids,
|
||||||
uid_format,
|
|
||||||
vcard_map,
|
vcard_map,
|
||||||
vcard_map_attrs,
|
vcard_map_attrs,
|
||||||
user_filter,
|
user_filter,
|
||||||
|
@ -552,10 +551,9 @@ search(State, Data) ->
|
||||||
Base = State#state.base,
|
Base = State#state.base,
|
||||||
SearchFilter = State#state.search_filter,
|
SearchFilter = State#state.search_filter,
|
||||||
Eldap_ID = State#state.eldap_id,
|
Eldap_ID = State#state.eldap_id,
|
||||||
UA = State#state.uid,
|
UIDs = State#state.uids,
|
||||||
UAF = State#state.uid_format,
|
|
||||||
ReportedAttrs = State#state.search_reported_attrs,
|
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},
|
case eldap:search(Eldap_ID, [{base, Base},
|
||||||
{filter, Filter},
|
{filter, Filter},
|
||||||
{attributes, ReportedAttrs}]) of
|
{attributes, ReportedAttrs}]) of
|
||||||
|
@ -569,8 +567,7 @@ search_items(Entries, State) ->
|
||||||
LServer = State#state.serverhost,
|
LServer = State#state.serverhost,
|
||||||
SearchReported = State#state.search_reported,
|
SearchReported = State#state.search_reported,
|
||||||
VCardMap = State#state.vcard_map,
|
VCardMap = State#state.vcard_map,
|
||||||
UIDAttr = State#state.uid,
|
UIDs = State#state.uids,
|
||||||
UIDAttrFormat = State#state.uid_format,
|
|
||||||
Attributes = lists:map(
|
Attributes = lists:map(
|
||||||
fun(E) ->
|
fun(E) ->
|
||||||
#eldap_entry{attributes = Attrs} = E,
|
#eldap_entry{attributes = Attrs} = E,
|
||||||
|
@ -578,12 +575,13 @@ search_items(Entries, State) ->
|
||||||
end, Entries),
|
end, Entries),
|
||||||
lists:flatmap(
|
lists:flatmap(
|
||||||
fun(Attrs) ->
|
fun(Attrs) ->
|
||||||
U = get_ldap_attr(UIDAttr, Attrs),
|
case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
|
||||||
case get_user_part(U, UIDAttrFormat) of
|
{U, UIDAttrFormat} ->
|
||||||
{ok, Username} ->
|
case eldap_utils:get_user_part(U, UIDAttrFormat) of
|
||||||
case ejabberd_auth:is_user_exists(Username, LServer) of
|
{ok, Username} ->
|
||||||
true ->
|
case ejabberd_auth:is_user_exists(Username, LServer) of
|
||||||
RFields = lists:map(
|
true ->
|
||||||
|
RFields = lists:map(
|
||||||
fun({_, VCardName}) ->
|
fun({_, VCardName}) ->
|
||||||
{VCardName,
|
{VCardName,
|
||||||
map_vcard_attr(
|
map_vcard_attr(
|
||||||
|
@ -592,41 +590,20 @@ search_items(Entries, State) ->
|
||||||
VCardMap,
|
VCardMap,
|
||||||
{Username, ?MYNAME})}
|
{Username, ?MYNAME})}
|
||||||
end, SearchReported),
|
end, SearchReported),
|
||||||
Result = [?FIELD("jid", Username ++ "@" ++ LServer)] ++
|
Result = [?FIELD("jid", Username ++ "@" ++ LServer)] ++
|
||||||
[?FIELD(Name, Value) || {Name, Value} <- RFields],
|
[?FIELD(Name, Value) || {Name, Value} <- RFields],
|
||||||
[{xmlelement, "item", [], Result}];
|
[{xmlelement, "item", [], Result}];
|
||||||
_ ->
|
_ ->
|
||||||
[]
|
[]
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
|
[]
|
||||||
|
end;
|
||||||
|
"" ->
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
end, Attributes).
|
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) ->
|
remove_user(_User) ->
|
||||||
true.
|
true.
|
||||||
|
|
||||||
|
@ -634,39 +611,15 @@ remove_user(_User) ->
|
||||||
%%% Auxiliary functions.
|
%%% 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) ->
|
map_vcard_attr(VCardName, Attributes, Pattern, UD) ->
|
||||||
Res = lists:filter(
|
Res = lists:filter(
|
||||||
fun({Name, _, _}) ->
|
fun({Name, _, _}) ->
|
||||||
case_insensitive_match(Name, VCardName)
|
eldap_utils:case_insensitive_match(Name, VCardName)
|
||||||
end, Pattern),
|
end, Pattern),
|
||||||
case Res of
|
case Res of
|
||||||
[{_, Str, Attrs}] ->
|
[{_, Str, Attrs}] ->
|
||||||
process_pattern(Str, UD,
|
process_pattern(Str, UD,
|
||||||
[get_ldap_attr(X, Attributes) || X<-Attrs]);
|
[eldap_utils:get_ldap_attr(X, Attributes) || X<-Attrs]);
|
||||||
_ -> ""
|
_ -> ""
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -674,16 +627,6 @@ process_pattern(Str, {User, Domain}, AttrValues) ->
|
||||||
eldap_filter:do_sub(Str,
|
eldap_filter:do_sub(Str,
|
||||||
[{"%s", V, 1} || V <- AttrValues] ++ [{"%u", User},{"%d", Domain}]).
|
[{"%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_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
||||||
find_xdata_el1(SubEls).
|
find_xdata_el1(SubEls).
|
||||||
|
|
||||||
|
@ -721,22 +664,14 @@ parse_options(Host, Opts) ->
|
||||||
ejabberd_config:get_local_option({ldap_base, Host});
|
ejabberd_config:get_local_option({ldap_base, Host});
|
||||||
B -> B
|
B -> B
|
||||||
end,
|
end,
|
||||||
UIDAttr = case gen_mod:get_opt(ldap_uidattr, Opts, undefined) of
|
UIDs = case gen_mod:get_opt(ldap_uids, Opts, undefined) of
|
||||||
undefined ->
|
undefined ->
|
||||||
case ejabberd_config:get_local_option({ldap_uidattr, Host}) of
|
case ejabberd_config:get_local_option({ldap_uids, Host}) of
|
||||||
undefined -> "uid";
|
undefined -> [{"uid", "%u"}];
|
||||||
UA -> UA
|
UI -> UI
|
||||||
end;
|
end;
|
||||||
UA -> UA
|
UI -> UI
|
||||||
end,
|
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,
|
|
||||||
RootDN = case gen_mod:get_opt(ldap_rootdn, Opts, undefined) of
|
RootDN = case gen_mod:get_opt(ldap_rootdn, Opts, undefined) of
|
||||||
undefined ->
|
undefined ->
|
||||||
case ejabberd_config:get_local_option({ldap_rootdn, Host}) of
|
case ejabberd_config:get_local_option({ldap_rootdn, Host}) of
|
||||||
|
@ -753,7 +688,7 @@ parse_options(Host, Opts) ->
|
||||||
end;
|
end;
|
||||||
Pass -> Pass
|
Pass -> Pass
|
||||||
end,
|
end,
|
||||||
SubFilter = "("++UIDAttr++"="++UIDAttrFormat++")",
|
SubFilter = lists:flatten(eldap_utils:generate_subfilter(UIDs)),
|
||||||
UserFilter = case gen_mod:get_opt(ldap_filter, Opts, undefined) of
|
UserFilter = case gen_mod:get_opt(ldap_filter, Opts, undefined) of
|
||||||
undefined ->
|
undefined ->
|
||||||
case ejabberd_config:get_local_option({ldap_filter, Host}) of
|
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 search requests we need to fetch only attributes defined
|
||||||
%% in vcard-map and search-reported. In some cases,
|
%% in vcard-map and search-reported. In some cases,
|
||||||
%% this will essentially reduce network traffic from an LDAP server.
|
%% this will essentially reduce network traffic from an LDAP server.
|
||||||
|
UIDAttrs = [UAttr || {UAttr, _} <- UIDs],
|
||||||
VCardMapAttrs = lists:usort(
|
VCardMapAttrs = lists:usort(
|
||||||
lists:append([A || {_, _, A} <- VCardMap]) ++ [UIDAttr]),
|
lists:append([A || {_, _, A} <- VCardMap]) ++ UIDAttrs),
|
||||||
SearchReportedAttrs =
|
SearchReportedAttrs =
|
||||||
lists:usort(lists:flatmap(
|
lists:usort(lists:flatmap(
|
||||||
fun({_, N}) ->
|
fun({_, N}) ->
|
||||||
|
@ -781,7 +717,7 @@ parse_options(Host, Opts) ->
|
||||||
{value, {_, _, L}} -> L;
|
{value, {_, _, L}} -> L;
|
||||||
_ -> []
|
_ -> []
|
||||||
end
|
end
|
||||||
end, SearchReported) ++ [UIDAttr]),
|
end, SearchReported) ++ UIDAttrs),
|
||||||
#state{serverhost = Host,
|
#state{serverhost = Host,
|
||||||
myhost = MyHost,
|
myhost = MyHost,
|
||||||
eldap_id = Eldap_ID,
|
eldap_id = Eldap_ID,
|
||||||
|
@ -791,8 +727,7 @@ parse_options(Host, Opts) ->
|
||||||
dn = RootDN,
|
dn = RootDN,
|
||||||
base = LDAPBase,
|
base = LDAPBase,
|
||||||
password = Password,
|
password = Password,
|
||||||
uid = UIDAttr,
|
uids = UIDs,
|
||||||
uid_format = UIDAttrFormat,
|
|
||||||
vcard_map = VCardMap,
|
vcard_map = VCardMap,
|
||||||
vcard_map_attrs = VCardMapAttrs,
|
vcard_map_attrs = VCardMapAttrs,
|
||||||
user_filter = UserFilter,
|
user_filter = UserFilter,
|
||||||
|
|
Loading…
Reference in New Issue