mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-26 17:38:45 +01:00
* examples/extauth/check_pass_null.pl: A reference "null"
implementation of external authentification script (thanks to Leif Johansson) * src/extauth.erl: Support for external authentification (thanks to Leif Johansson) * src/ejabberd_auth.erl: Likewise * src/mod_vcard_ldap.erl: A drop-in replacement for mod_vcard.erl which uses ldap for JUD and vCard (thanks to Leif Johansson) SVN Revision: 251
This commit is contained in:
parent
76f00658f2
commit
6cd02b9714
13
ChangeLog
13
ChangeLog
@ -1,3 +1,16 @@
|
|||||||
|
2004-07-30 Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
|
||||||
|
* examples/extauth/check_pass_null.pl: A reference "null"
|
||||||
|
implementation of external authentification script (thanks to Leif
|
||||||
|
Johansson)
|
||||||
|
|
||||||
|
* src/extauth.erl: Support for external authentification
|
||||||
|
(thanks to Leif Johansson)
|
||||||
|
* src/ejabberd_auth.erl: Likewise
|
||||||
|
|
||||||
|
* src/mod_vcard_ldap.erl: A drop-in replacement for mod_vcard.erl
|
||||||
|
which uses ldap for JUD and vCard (thanks to Leif Johansson)
|
||||||
|
|
||||||
2004-07-28 Alexey Shchepin <alexey@sevcom.net>
|
2004-07-28 Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
|
||||||
* src/tls/tls_drv.c: Added freeing of SSL stuff
|
* src/tls/tls_drv.c: Added freeing of SSL stuff
|
||||||
|
50
examples/extauth/check_pass_null.pl
Normal file
50
examples/extauth/check_pass_null.pl
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/local/bin/perl
|
||||||
|
|
||||||
|
use Unix::Syslog qw(:macros :subs);
|
||||||
|
|
||||||
|
my $domain = $ARGV[0] || "example.com";
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
# my $rin = '',$rout;
|
||||||
|
# vec($rin,fileno(STDIN),1) = 1;
|
||||||
|
# $ein = $rin;
|
||||||
|
# my $nfound = select($rout=$rin,undef,undef,undef);
|
||||||
|
|
||||||
|
my $buf = "";
|
||||||
|
syslog LOG_INFO,"waiting for packet";
|
||||||
|
my $nread = sysread STDIN,$buf,2;
|
||||||
|
do { syslog LOG_INFO,"port closed"; exit; } unless $nread == 2;
|
||||||
|
my $len = unpack "n",$buf;
|
||||||
|
my $nread = sysread STDIN,$buf,$len;
|
||||||
|
|
||||||
|
my ($op,$user,$password) = split /:/,$buf;
|
||||||
|
#$user =~ s/\./\//og;
|
||||||
|
my $jid = "$user\@$domain";
|
||||||
|
my $result;
|
||||||
|
|
||||||
|
syslog(LOG_INFO,"request (%s)", $op);
|
||||||
|
|
||||||
|
SWITCH:
|
||||||
|
{
|
||||||
|
$op eq 'auth' and do
|
||||||
|
{
|
||||||
|
$result = 1;
|
||||||
|
},last SWITCH;
|
||||||
|
|
||||||
|
$op eq 'setpass' and do
|
||||||
|
{
|
||||||
|
$result = 1;
|
||||||
|
},last SWITCH;
|
||||||
|
|
||||||
|
$op eq 'isuser' and do
|
||||||
|
{
|
||||||
|
# password is null. Return 1 if the user $user\@$domain exitst.
|
||||||
|
$result = 1;
|
||||||
|
},last SWITCH;
|
||||||
|
};
|
||||||
|
my $out = pack "nn",2,$result ? 1 : 0;
|
||||||
|
syswrite STDOUT,$out;
|
||||||
|
}
|
||||||
|
|
||||||
|
closelog;
|
@ -47,7 +47,14 @@
|
|||||||
%%% API
|
%%% API
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
start() ->
|
start() ->
|
||||||
|
case auth_method() of
|
||||||
|
external ->
|
||||||
|
extauth:start(ejabberd_config:get_local_option(extauth_program));
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
gen_server:start({local, ejabberd_auth}, ejabberd_auth, [], []).
|
gen_server:start({local, ejabberd_auth}, ejabberd_auth, [], []).
|
||||||
|
|
||||||
start_link() ->
|
start_link() ->
|
||||||
gen_server:start_link({local, ejabberd_auth}, ejabberd_auth, [], []).
|
gen_server:start_link({local, ejabberd_auth}, ejabberd_auth, [], []).
|
||||||
|
|
||||||
@ -68,6 +75,8 @@ init([]) ->
|
|||||||
case auth_method() of
|
case auth_method() of
|
||||||
internal ->
|
internal ->
|
||||||
ok;
|
ok;
|
||||||
|
external ->
|
||||||
|
ok;
|
||||||
ldap ->
|
ldap ->
|
||||||
LDAPServers = ejabberd_config:get_local_option(ldap_servers),
|
LDAPServers = ejabberd_config:get_local_option(ldap_servers),
|
||||||
eldap:start_link("ejabberd", LDAPServers, 389, "", ""),
|
eldap:start_link("ejabberd", LDAPServers, 389, "", ""),
|
||||||
@ -124,6 +133,16 @@ terminate(_Reason, _State) ->
|
|||||||
|
|
||||||
auth_method() ->
|
auth_method() ->
|
||||||
case ejabberd_config:get_local_option(auth_method) of
|
case ejabberd_config:get_local_option(auth_method) of
|
||||||
|
external ->
|
||||||
|
external;
|
||||||
|
ldap ->
|
||||||
|
ldap;
|
||||||
|
_ ->
|
||||||
|
internal
|
||||||
|
end.
|
||||||
|
|
||||||
|
user_method() ->
|
||||||
|
case ejabberd_config:get_local_option(user_method) of
|
||||||
ldap ->
|
ldap ->
|
||||||
ldap;
|
ldap;
|
||||||
_ ->
|
_ ->
|
||||||
@ -134,19 +153,31 @@ plain_password_required() ->
|
|||||||
case auth_method() of
|
case auth_method() of
|
||||||
internal ->
|
internal ->
|
||||||
false;
|
false;
|
||||||
|
external ->
|
||||||
|
true;
|
||||||
ldap ->
|
ldap ->
|
||||||
true
|
true
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
check_password(User, Password) ->
|
check_password(User, Password) ->
|
||||||
case auth_method() of
|
case auth_method() of
|
||||||
internal ->
|
internal ->
|
||||||
check_password_internal(User, Password);
|
check_password_internal(User, Password);
|
||||||
|
external ->
|
||||||
|
check_password_external(User, Password);
|
||||||
ldap ->
|
ldap ->
|
||||||
check_password_ldap(User, Password)
|
check_password_ldap(User, Password)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
check_password_external(User, Password) ->
|
||||||
|
extauth:check_password(User, Password).
|
||||||
|
|
||||||
|
set_password_external(User, Password) ->
|
||||||
|
extauth:set_password(User, Password).
|
||||||
|
|
||||||
|
is_user_exists_external(User) ->
|
||||||
|
extauth:is_user_exists(User).
|
||||||
|
|
||||||
check_password_internal(User, Password) ->
|
check_password_internal(User, Password) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||||
@ -160,6 +191,8 @@ check_password(User, Password, StreamID, Digest) ->
|
|||||||
case auth_method() of
|
case auth_method() of
|
||||||
internal ->
|
internal ->
|
||||||
check_password_internal(User, Password, StreamID, Digest);
|
check_password_internal(User, Password, StreamID, Digest);
|
||||||
|
external ->
|
||||||
|
check_password_external(User, Password, StreamID, Digest);
|
||||||
ldap ->
|
ldap ->
|
||||||
check_password_ldap(User, Password, StreamID, Digest)
|
check_password_ldap(User, Password, StreamID, Digest)
|
||||||
end.
|
end.
|
||||||
@ -183,8 +216,16 @@ check_password_internal(User, Password, StreamID, Digest) ->
|
|||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
set_password(User, Password) ->
|
set_password(User, Password) ->
|
||||||
|
case auth_method() of
|
||||||
|
internal ->
|
||||||
|
set_password_internal(User,Password);
|
||||||
|
external ->
|
||||||
|
set_password_external(User,Password);
|
||||||
|
ldap -> {error, not_allowed}
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_password_internal(User, Password) ->
|
||||||
case jlib:nodeprep(User) of
|
case jlib:nodeprep(User) of
|
||||||
error -> {error, invalid_jid};
|
error -> {error, invalid_jid};
|
||||||
LUser ->
|
LUser ->
|
||||||
@ -200,6 +241,8 @@ try_register(User, Password) ->
|
|||||||
case auth_method() of
|
case auth_method() of
|
||||||
internal ->
|
internal ->
|
||||||
try_register_internal(User, Password);
|
try_register_internal(User, Password);
|
||||||
|
external ->
|
||||||
|
{error, not_allowed};
|
||||||
ldap ->
|
ldap ->
|
||||||
{error, not_allowed}
|
{error, not_allowed}
|
||||||
end.
|
end.
|
||||||
@ -246,6 +289,8 @@ is_user_exists(User) ->
|
|||||||
case auth_method() of
|
case auth_method() of
|
||||||
internal ->
|
internal ->
|
||||||
is_user_exists_internal(User);
|
is_user_exists_internal(User);
|
||||||
|
external ->
|
||||||
|
is_user_exists_external(User);
|
||||||
ldap ->
|
ldap ->
|
||||||
is_user_exists_ldap(User)
|
is_user_exists_ldap(User)
|
||||||
end.
|
end.
|
||||||
@ -262,7 +307,7 @@ is_user_exists_internal(User) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
remove_user(User) ->
|
remove_user(User) ->
|
||||||
case auth_method() of
|
case user_method() of
|
||||||
internal ->
|
internal ->
|
||||||
remove_user_internal(User);
|
remove_user_internal(User);
|
||||||
ldap ->
|
ldap ->
|
||||||
@ -282,7 +327,7 @@ remove_user_internal(User) ->
|
|||||||
catch mod_private:remove_user(User).
|
catch mod_private:remove_user(User).
|
||||||
|
|
||||||
remove_user(User, Password) ->
|
remove_user(User, Password) ->
|
||||||
case auth_method() of
|
case user_method() of
|
||||||
internal ->
|
internal ->
|
||||||
remove_user_internal(User, Password);
|
remove_user_internal(User, Password);
|
||||||
ldap ->
|
ldap ->
|
||||||
@ -322,6 +367,9 @@ remove_user_internal(User, Password) ->
|
|||||||
check_password_ldap(User, Password, StreamID, Digest) ->
|
check_password_ldap(User, Password, StreamID, Digest) ->
|
||||||
check_password_ldap(User, Password).
|
check_password_ldap(User, Password).
|
||||||
|
|
||||||
|
check_password_external(User, Password, StreamID, Digest) ->
|
||||||
|
check_password_external(User, Password).
|
||||||
|
|
||||||
check_password_ldap(User, Password) ->
|
check_password_ldap(User, Password) ->
|
||||||
case find_user_dn(User) of
|
case find_user_dn(User) of
|
||||||
false ->
|
false ->
|
||||||
|
76
src/extauth.erl
Normal file
76
src/extauth.erl
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% File : extauth.erl
|
||||||
|
%%% Author : Leif Johansson <leifj@it.su.se>
|
||||||
|
%%% Purpose : External authentication using a simple port-driver
|
||||||
|
%%% Created : 30 Jul 2004 by Leif Johansson <leifj@it.su.se>
|
||||||
|
%%% Id : $Id$
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(extauth).
|
||||||
|
-author('leifj@it.su.se').
|
||||||
|
|
||||||
|
-export([start/1, stop/0, init/1,
|
||||||
|
check_password/2, set_password/2, is_user_exists/1 ]).
|
||||||
|
|
||||||
|
|
||||||
|
start(ExtPrg) ->
|
||||||
|
spawn(?MODULE, init, [ExtPrg]).
|
||||||
|
|
||||||
|
init(ExtPrg) ->
|
||||||
|
register(eauth,self()),
|
||||||
|
process_flag(trap_exit,true),
|
||||||
|
Port = open_port({spawn, ExtPrg}, [{packet,2}]),
|
||||||
|
loop(Port).
|
||||||
|
|
||||||
|
stop() ->
|
||||||
|
eauth ! stop.
|
||||||
|
|
||||||
|
check_password(User,Password) ->
|
||||||
|
call_port(["auth",User,Password]).
|
||||||
|
|
||||||
|
is_user_exists(User) ->
|
||||||
|
call_port(["isuser",User]).
|
||||||
|
|
||||||
|
set_password(User,Password) ->
|
||||||
|
call_port(["setpass",User,Password]).
|
||||||
|
|
||||||
|
call_port(Msg) ->
|
||||||
|
eauth ! {call, self(), Msg},
|
||||||
|
receive
|
||||||
|
{eauth,Result}->
|
||||||
|
Result
|
||||||
|
end.
|
||||||
|
|
||||||
|
loop(Port) ->
|
||||||
|
receive
|
||||||
|
{call, Caller, Msg} ->
|
||||||
|
Port ! {self(), {command, encode(Msg)}},
|
||||||
|
receive
|
||||||
|
{Port, {data, Data}} ->
|
||||||
|
Caller ! {eauth, decode(Data)}
|
||||||
|
end,
|
||||||
|
loop(Port);
|
||||||
|
stop ->
|
||||||
|
Port ! {self(), close},
|
||||||
|
receive
|
||||||
|
{Port, closed} ->
|
||||||
|
exit(normal)
|
||||||
|
end;
|
||||||
|
{'EXIT', Port, Reason} ->
|
||||||
|
io:format("~p ~n", [Reason]),
|
||||||
|
exit(port_terminated)
|
||||||
|
end.
|
||||||
|
|
||||||
|
join(List, Sep) ->
|
||||||
|
lists:foldl(fun(A, "") -> A;
|
||||||
|
(A, Acc) -> Acc ++ Sep ++ A
|
||||||
|
end, "", List).
|
||||||
|
|
||||||
|
encode(L) ->
|
||||||
|
join(L,":").
|
||||||
|
|
||||||
|
decode([0,0]) ->
|
||||||
|
false;
|
||||||
|
decode([0,1]) ->
|
||||||
|
true.
|
||||||
|
|
572
src/mod_vcard_ldap.erl
Normal file
572
src/mod_vcard_ldap.erl
Normal file
@ -0,0 +1,572 @@
|
|||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% File : mod_vcard_ldap.erl
|
||||||
|
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
%%% Purpose :
|
||||||
|
%%% Created : 2 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
%%% Id : $Id$
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(mod_vcard_ldap).
|
||||||
|
-author('alexey@sevcom.net').
|
||||||
|
-vsn('$Revision$ ').
|
||||||
|
|
||||||
|
-behaviour(gen_mod).
|
||||||
|
|
||||||
|
-export([start/1, init/2, stop/0,
|
||||||
|
process_local_iq/3,
|
||||||
|
process_sm_iq/3,
|
||||||
|
remove_user/1]).
|
||||||
|
|
||||||
|
-include("ejabberd.hrl").
|
||||||
|
-include("eldap/eldap.hrl").
|
||||||
|
-include("jlib.hrl").
|
||||||
|
|
||||||
|
|
||||||
|
start(Opts) ->
|
||||||
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||||
|
gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_VCARD,
|
||||||
|
?MODULE, process_local_iq, IQDisc),
|
||||||
|
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_VCARD,
|
||||||
|
?MODULE, process_sm_iq, IQDisc),
|
||||||
|
LDAPServers = ejabberd_config:get_local_option(ldap_servers),
|
||||||
|
eldap:start_link("mod_vcard_ldap", LDAPServers, 389, "", ""),
|
||||||
|
Host = gen_mod:get_opt(host, Opts, "vjud." ++ ?MYNAME),
|
||||||
|
Search = gen_mod:get_opt(search, Opts, true),
|
||||||
|
register(ejabberd_mod_vcard_ldap, spawn(?MODULE, init, [Host, Search])).
|
||||||
|
|
||||||
|
init(Host, Search) ->
|
||||||
|
case Search of
|
||||||
|
false ->
|
||||||
|
loop(Host);
|
||||||
|
_ ->
|
||||||
|
ejabberd_router:register_route(Host),
|
||||||
|
loop(Host)
|
||||||
|
end.
|
||||||
|
|
||||||
|
loop(Host) ->
|
||||||
|
receive
|
||||||
|
{route, From, To, Packet} ->
|
||||||
|
case catch do_route(From, To, Packet) of
|
||||||
|
{'EXIT', Reason} ->
|
||||||
|
?ERROR_MSG("~p", [Reason]);
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
loop(Host);
|
||||||
|
stop ->
|
||||||
|
catch ejabberd_router:unregister_route(Host),
|
||||||
|
ok;
|
||||||
|
_ ->
|
||||||
|
loop(Host)
|
||||||
|
end.
|
||||||
|
|
||||||
|
stop() ->
|
||||||
|
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_VCARD),
|
||||||
|
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_VCARD),
|
||||||
|
ejabberd_mod_vcard_ldap ! stop,
|
||||||
|
ok.
|
||||||
|
|
||||||
|
process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||||
|
case Type of
|
||||||
|
set ->
|
||||||
|
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||||
|
get ->
|
||||||
|
IQ#iq{type = result,
|
||||||
|
sub_el = [{xmlelement, "vCard",
|
||||||
|
[{"xmlns", ?NS_VCARD}],
|
||||||
|
[{xmlelement, "FN", [],
|
||||||
|
[{xmlcdata, "ejabberd"}]},
|
||||||
|
{xmlelement, "URL", [],
|
||||||
|
[{xmlcdata,
|
||||||
|
"http://ejabberd.jabberstudio.org/"}]},
|
||||||
|
{xmlelement, "DESC", [],
|
||||||
|
[{xmlcdata,
|
||||||
|
translate:translate(
|
||||||
|
Lang,
|
||||||
|
"Erlang Jabber Server\n"
|
||||||
|
"Copyright (c) 2002-2004 Alexey Shchepin")}]},
|
||||||
|
{xmlelement, "BDAY", [],
|
||||||
|
[{xmlcdata, "2002-11-16"}]}
|
||||||
|
]}]}
|
||||||
|
end.
|
||||||
|
|
||||||
|
find_ldap_user(User) ->
|
||||||
|
Attr = ejabberd_config:get_local_option(ldap_uidattr),
|
||||||
|
Filter = eldap:equalityMatch(Attr, User),
|
||||||
|
Base = ejabberd_config:get_local_option(ldap_base),
|
||||||
|
case eldap:search("mod_vcard_ldap", [{base, Base},
|
||||||
|
{filter, Filter},
|
||||||
|
{attributes, []}]) of
|
||||||
|
#eldap_search_result{entries = [E | _]} ->
|
||||||
|
E;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
|
is_attribute_read_allowed(Name,From,To) ->
|
||||||
|
true.
|
||||||
|
|
||||||
|
ldap_attribute_to_vcard(Prefix,{Name,Values},From,To) ->
|
||||||
|
case is_attribute_read_allowed(Name,From,To) of
|
||||||
|
true ->
|
||||||
|
ldap_lca_to_vcard(Prefix,stringprep:tolower(Name),Values);
|
||||||
|
_ ->
|
||||||
|
none
|
||||||
|
end.
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(vCard,"displayname",[Value|_]) ->
|
||||||
|
{xmlelement,"FN",[],[{xmlcdata,Value}]};
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(vCard,"uid",[Value|_]) ->
|
||||||
|
{xmlelement,"NICKNAME",[],[{xmlcdata,Value}]};
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(vCard,"title",[Value|_]) ->
|
||||||
|
{xmlelement,"TITLE",[],[{xmlcdata,Value}]};
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(vCard,"labeleduri",[Value|_]) ->
|
||||||
|
{xmlelement,"URL",[],[{xmlcdata,Value}]};
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(vCard,"description",[Value|_]) ->
|
||||||
|
{xmlelement,"DESC",[],[{xmlcdata,Value}]};
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(vCard,"telephonenumber",[Value|_]) ->
|
||||||
|
{xmlelement,"TEL",[],[{xmlelement,"VOICE",[],[]},
|
||||||
|
{xmlelement,"WORK",[],[]},
|
||||||
|
{xmlelement,"NUMBER",[],[{xmlcdata,Value}]}]};
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(vCard,"mail",[Value|_]) ->
|
||||||
|
{xmlelement,"EMAIL",[],[{xmlelement,"INTERNET",[],[]},
|
||||||
|
{xmlelement,"PREF",[],[]},
|
||||||
|
{xmlelement,"USERID",[],[{xmlcdata,Value}]}]};
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(vCardN,"sn",[Value|_]) ->
|
||||||
|
{xmlelement,"FAMILY",[],[{xmlcdata,Value}]};
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(vCardN,"givenname",[Value|_]) ->
|
||||||
|
{xmlelement,"GIVEN",[],[{xmlcdata,Value}]};
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(vCardN,"initials",[Value|_]) ->
|
||||||
|
{xmlelement,"MIDDLE",[],[{xmlcdata,Value}]};
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(vCardO,"o",[Value|_]) ->
|
||||||
|
{xmlelement,"ORGNAME",[],[{xmlcdata,Value}]};
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(vCardO,"ou",[Value|_]) ->
|
||||||
|
{xmlelement,"ORGUNIT",[],[{xmlcdata,Value}]};
|
||||||
|
|
||||||
|
ldap_lca_to_vcard(_,_,_) -> none.
|
||||||
|
|
||||||
|
ldap_attributes_to_vcard(Attributes,From,To) ->
|
||||||
|
Elts = lists:map(fun(Attr) ->
|
||||||
|
ldap_attribute_to_vcard(vCard,Attr,From,To)
|
||||||
|
end,Attributes),
|
||||||
|
FElts = [ X || X <- Elts, X /= none ],
|
||||||
|
NElts = lists:map(fun(Attr) ->
|
||||||
|
ldap_attribute_to_vcard(vCardN,Attr,From,To)
|
||||||
|
end,Attributes),
|
||||||
|
FNElts = [ X || X <- NElts, X /= none ],
|
||||||
|
OElts = lists:map(fun(Attr) ->
|
||||||
|
ldap_attribute_to_vcard(vCardO,Attr,From,To)
|
||||||
|
end,Attributes),
|
||||||
|
FOElts = [ X || X <- OElts, X /= none ],
|
||||||
|
[{xmlelement, "vCard", [{"xmlns", ?NS_VCARD}],
|
||||||
|
lists:append(FElts,
|
||||||
|
[{xmlelement,"N",[],FNElts},
|
||||||
|
{xmlelement,"ORG",[],FOElts}])
|
||||||
|
}].
|
||||||
|
|
||||||
|
is_self_request(From,To) ->
|
||||||
|
#jid{luser = RUser, lserver = RServer } = From,
|
||||||
|
#jid{luser = LUser} = To,
|
||||||
|
case RServer == ?MYNAME of
|
||||||
|
true ->
|
||||||
|
LUser == RUser;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
|
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||||
|
case Type of
|
||||||
|
set ->
|
||||||
|
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||||
|
get ->
|
||||||
|
#jid{luser = LUser} = To,
|
||||||
|
case find_ldap_user(LUser) of
|
||||||
|
#eldap_entry{attributes = Attributes} ->
|
||||||
|
Vcard = ldap_attributes_to_vcard(Attributes,From,To),
|
||||||
|
IQ#iq{type = result, sub_el = Vcard};
|
||||||
|
_ -> IQ#iq{type = result, sub_el = []}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
-define(TLFIELD(Type, Label, Var),
|
||||||
|
{xmlelement, "field", [{"type", Type},
|
||||||
|
{"label", translate:translate(Lang, Label)},
|
||||||
|
{"var", Var}], []}).
|
||||||
|
|
||||||
|
|
||||||
|
-define(FORM(JID),
|
||||||
|
[{xmlelement, "instructions", [],
|
||||||
|
[{xmlcdata, translate:translate(Lang, "You need an x:data capable client to search")}]},
|
||||||
|
{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
|
||||||
|
[{xmlelement, "title", [],
|
||||||
|
[{xmlcdata, translate:translate(Lang, "Search users in ") ++
|
||||||
|
jlib:jid_to_string(JID)}]},
|
||||||
|
{xmlelement, "instructions", [],
|
||||||
|
[{xmlcdata, translate:translate(Lang, "Fill in fields to search "
|
||||||
|
"for any matching Jabber User")}]},
|
||||||
|
?TLFIELD("text-single", "User", "user"),
|
||||||
|
?TLFIELD("text-single", "Full Name", "fn"),
|
||||||
|
?TLFIELD("text-single", "Given Name", "given"),
|
||||||
|
?TLFIELD("text-single", "Middle Name", "middle"),
|
||||||
|
?TLFIELD("text-single", "Family Name", "family"),
|
||||||
|
?TLFIELD("text-single", "Nickname", "nickname"),
|
||||||
|
?TLFIELD("text-single", "Birthday", "bday"),
|
||||||
|
?TLFIELD("text-single", "Country", "ctry"),
|
||||||
|
?TLFIELD("text-single", "City", "locality"),
|
||||||
|
?TLFIELD("text-single", "email", "email"),
|
||||||
|
?TLFIELD("text-single", "Organization Name", "orgname"),
|
||||||
|
?TLFIELD("text-single", "Organization Unit", "orgunit")
|
||||||
|
]}]).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
do_route(From, To, Packet) ->
|
||||||
|
#jid{user = User, resource = Resource} = To,
|
||||||
|
if
|
||||||
|
(User /= "") or (Resource /= "") ->
|
||||||
|
Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||||
|
ejabberd_router ! {route, To, From, Err};
|
||||||
|
true ->
|
||||||
|
IQ = jlib:iq_query_info(Packet),
|
||||||
|
case IQ of
|
||||||
|
#iq{type = Type, xmlns = ?NS_SEARCH, lang = Lang, sub_el = SubEl} ->
|
||||||
|
case Type of
|
||||||
|
set ->
|
||||||
|
XDataEl = find_xdata_el(SubEl),
|
||||||
|
case XDataEl of
|
||||||
|
false ->
|
||||||
|
Err = jlib:make_error_reply(
|
||||||
|
Packet, ?ERR_BAD_REQUEST),
|
||||||
|
ejabberd_router:route(To, From, Err);
|
||||||
|
_ ->
|
||||||
|
XData = jlib:parse_xdata_submit(XDataEl),
|
||||||
|
case XData of
|
||||||
|
invalid ->
|
||||||
|
Err = jlib:make_error_reply(
|
||||||
|
Packet,
|
||||||
|
?ERR_BAD_REQUEST),
|
||||||
|
ejabberd_router:route(To, From,
|
||||||
|
Err);
|
||||||
|
_ ->
|
||||||
|
ResIQ =
|
||||||
|
IQ#iq{
|
||||||
|
type = result,
|
||||||
|
sub_el =
|
||||||
|
[{xmlelement,
|
||||||
|
"query",
|
||||||
|
[{"xmlns", ?NS_SEARCH}],
|
||||||
|
[{xmlelement, "x",
|
||||||
|
[{"xmlns", ?NS_XDATA},
|
||||||
|
{"type", "result"}],
|
||||||
|
search_result(Lang, To, XData)
|
||||||
|
}]}]},
|
||||||
|
ejabberd_router:route(
|
||||||
|
To, From, jlib:iq_to_xml(ResIQ))
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
get ->
|
||||||
|
ResIQ = IQ#iq{type = result,
|
||||||
|
sub_el = [{xmlelement,
|
||||||
|
"query",
|
||||||
|
[{"xmlns", ?NS_SEARCH}],
|
||||||
|
?FORM(To)
|
||||||
|
}]},
|
||||||
|
ejabberd_router:route(To,
|
||||||
|
From,
|
||||||
|
jlib:iq_to_xml(ResIQ))
|
||||||
|
end;
|
||||||
|
#iq{type = Type, xmlns = ?NS_DISCO_INFO, sub_el = SubEl} ->
|
||||||
|
case Type of
|
||||||
|
set ->
|
||||||
|
Err = jlib:make_error_reply(
|
||||||
|
Packet, ?ERR_NOT_ALLOWED),
|
||||||
|
ejabberd_router:route(To, From, Err);
|
||||||
|
get ->
|
||||||
|
ResIQ =
|
||||||
|
IQ#iq{type = result,
|
||||||
|
sub_el = [{xmlelement,
|
||||||
|
"query",
|
||||||
|
[{"xmlns", ?NS_DISCO_INFO}],
|
||||||
|
[{xmlelement, "identity",
|
||||||
|
[{"category", "directory"},
|
||||||
|
{"type", "user"},
|
||||||
|
{"name",
|
||||||
|
"vCard User Search"}],
|
||||||
|
[]},
|
||||||
|
{xmlelement, "feature",
|
||||||
|
[{"var", ?NS_SEARCH}], []},
|
||||||
|
{xmlelement, "feature",
|
||||||
|
[{"var", ?NS_VCARD}], []}
|
||||||
|
]
|
||||||
|
}]},
|
||||||
|
ejabberd_router:route(To,
|
||||||
|
From,
|
||||||
|
jlib:iq_to_xml(ResIQ))
|
||||||
|
end;
|
||||||
|
#iq{type = Type, xmlns = ?NS_DISCO_ITEMS, sub_el = SubEl} ->
|
||||||
|
case Type of
|
||||||
|
set ->
|
||||||
|
Err = jlib:make_error_reply(
|
||||||
|
Packet, ?ERR_NOT_ALLOWED),
|
||||||
|
ejabberd_router:route(To, From, Err);
|
||||||
|
get ->
|
||||||
|
ResIQ =
|
||||||
|
IQ#iq{type = result,
|
||||||
|
sub_el = [{xmlelement,
|
||||||
|
"query",
|
||||||
|
[{"xmlns", ?NS_DISCO_INFO}],
|
||||||
|
[]}]},
|
||||||
|
ejabberd_router:route(To,
|
||||||
|
From,
|
||||||
|
jlib:iq_to_xml(ResIQ))
|
||||||
|
end;
|
||||||
|
#iq{type = get, xmlns = ?NS_VCARD, lang = Lang} ->
|
||||||
|
ResIQ =
|
||||||
|
IQ#iq{type = result,
|
||||||
|
sub_el = [{xmlelement,
|
||||||
|
"vCard",
|
||||||
|
[{"xmlns", ?NS_VCARD}],
|
||||||
|
iq_get_vcard(Lang)}]},
|
||||||
|
ejabberd_router:route(To,
|
||||||
|
From,
|
||||||
|
jlib:iq_to_xml(ResIQ));
|
||||||
|
_ ->
|
||||||
|
Err = jlib:make_error_reply(Packet,
|
||||||
|
?ERR_SERVICE_UNAVAILABLE),
|
||||||
|
ejabberd_router:route(To, From, Err)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
iq_get_vcard(Lang) ->
|
||||||
|
[{xmlelement, "FN", [],
|
||||||
|
[{xmlcdata, "ejabberd/mod_vcard"}]},
|
||||||
|
{xmlelement, "URL", [],
|
||||||
|
[{xmlcdata,
|
||||||
|
"http://ejabberd.jabberstudio.org/"}]},
|
||||||
|
{xmlelement, "DESC", [],
|
||||||
|
[{xmlcdata, translate:translate(
|
||||||
|
Lang,
|
||||||
|
"ejabberd vCard module\n"
|
||||||
|
"Copyright (c) 2003-2004 Alexey Shchepin")}]}].
|
||||||
|
|
||||||
|
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
||||||
|
find_xdata_el1(SubEls).
|
||||||
|
|
||||||
|
find_xdata_el1([]) ->
|
||||||
|
false;
|
||||||
|
find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
|
||||||
|
case xml:get_attr_s("xmlns", Attrs) of
|
||||||
|
?NS_XDATA ->
|
||||||
|
{xmlelement, Name, Attrs, SubEls};
|
||||||
|
_ ->
|
||||||
|
find_xdata_el1(Els)
|
||||||
|
end;
|
||||||
|
find_xdata_el1([_ | Els]) ->
|
||||||
|
find_xdata_el1(Els).
|
||||||
|
|
||||||
|
-define(LFIELD(Label, Var),
|
||||||
|
{xmlelement, "field", [{"label", translate:translate(Lang, Label)},
|
||||||
|
{"var", Var}], []}).
|
||||||
|
|
||||||
|
search_result(Lang, JID, Data) ->
|
||||||
|
[{xmlelement, "title", [],
|
||||||
|
[{xmlcdata, translate:translate(Lang, "Results of search in ") ++
|
||||||
|
jlib:jid_to_string(JID)}]},
|
||||||
|
{xmlelement, "reported", [],
|
||||||
|
[?LFIELD("JID", "jid"),
|
||||||
|
?LFIELD("Full Name", "fn"),
|
||||||
|
?LFIELD("Given Name", "given"),
|
||||||
|
?LFIELD("Middle Name", "middle"),
|
||||||
|
?LFIELD("Family Name", "family"),
|
||||||
|
?LFIELD("Nickname", "nickname"),
|
||||||
|
?LFIELD("Birthday", "bday"),
|
||||||
|
?LFIELD("Country", "ctry"),
|
||||||
|
?LFIELD("City", "locality"),
|
||||||
|
?LFIELD("email", "email"),
|
||||||
|
?LFIELD("Organization Name", "orgname"),
|
||||||
|
?LFIELD("Organization Unit", "orgunit")
|
||||||
|
]}] ++ lists:map(fun(E) ->
|
||||||
|
record_to_item(E#eldap_entry.attributes)
|
||||||
|
end, search(Data)).
|
||||||
|
|
||||||
|
-define(FIELD(Var, Val),
|
||||||
|
{xmlelement, "field", [{"var", Var}],
|
||||||
|
[{xmlelement, "value", [],
|
||||||
|
[{xmlcdata, Val}]}]}).
|
||||||
|
|
||||||
|
case_exact_compare(none,_) ->
|
||||||
|
false;
|
||||||
|
case_exact_compare(_,none) ->
|
||||||
|
false;
|
||||||
|
case_exact_compare(X,Y) ->
|
||||||
|
X > Y.
|
||||||
|
|
||||||
|
ldap_sort_entries(L) ->
|
||||||
|
lists:sort(fun(E1,E2) ->
|
||||||
|
case_exact_compare(ldap_get_value(E1,"cn"),ldap_get_value(E2,"cn"))
|
||||||
|
end,L).
|
||||||
|
|
||||||
|
ldap_get_value(E,Attribute) ->
|
||||||
|
#eldap_entry{attributes = Attributes} = E,
|
||||||
|
case lists:filter(fun({A,_}) ->
|
||||||
|
string:equal(A,Attribute)
|
||||||
|
end,Attributes) of
|
||||||
|
[{Attr,[Value|_]}] ->
|
||||||
|
Value;
|
||||||
|
_ ->
|
||||||
|
none
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
ldap_attribute_to_item("uid",Value) ->
|
||||||
|
[
|
||||||
|
?FIELD("jid",Value ++ "@" ++ ?MYNAME),
|
||||||
|
?FIELD("uid",Value),
|
||||||
|
?FIELD("nickname",Value)
|
||||||
|
];
|
||||||
|
|
||||||
|
ldap_attribute_to_item("displayname",Value) ->
|
||||||
|
[
|
||||||
|
?FIELD("fn",Value)
|
||||||
|
];
|
||||||
|
|
||||||
|
ldap_attribute_to_item("sn",Value) ->
|
||||||
|
[
|
||||||
|
?FIELD("family",Value)
|
||||||
|
];
|
||||||
|
|
||||||
|
ldap_attribute_to_item("displayname",Value) ->
|
||||||
|
[
|
||||||
|
?FIELD("fn",Value)
|
||||||
|
];
|
||||||
|
|
||||||
|
ldap_attribute_to_item("givenname",Value) ->
|
||||||
|
[
|
||||||
|
?FIELD("given",Value)
|
||||||
|
];
|
||||||
|
|
||||||
|
ldap_attribute_to_item("initials",Value) ->
|
||||||
|
[
|
||||||
|
?FIELD("middle",Value)
|
||||||
|
];
|
||||||
|
|
||||||
|
ldap_attribute_to_item("mail",Value) ->
|
||||||
|
[
|
||||||
|
?FIELD("email",Value)
|
||||||
|
];
|
||||||
|
|
||||||
|
ldap_attribute_to_item("o",Value) ->
|
||||||
|
[
|
||||||
|
?FIELD("orgname",Value)
|
||||||
|
];
|
||||||
|
|
||||||
|
ldap_attribute_to_item("ou",Value) ->
|
||||||
|
[
|
||||||
|
?FIELD("orgunit",Value)
|
||||||
|
];
|
||||||
|
|
||||||
|
ldap_attribute_to_item(_,_) ->
|
||||||
|
[none].
|
||||||
|
|
||||||
|
record_to_item(Attributes) ->
|
||||||
|
List = lists:append(lists:map(fun({Attr,[Value|_]}) ->
|
||||||
|
ldap_attribute_to_item(stringprep:tolower(Attr),Value)
|
||||||
|
end,Attributes)),
|
||||||
|
FList = [X || X <- List, X /= none],
|
||||||
|
{xmlelement, "item", [],FList}.
|
||||||
|
|
||||||
|
search(Data) ->
|
||||||
|
Filter = make_filter(Data),
|
||||||
|
Base = ejabberd_config:get_local_option(ldap_base),
|
||||||
|
UIDAttr = ejabberd_config:get_local_option(ldap_uidattr),
|
||||||
|
case eldap:search("mod_vcard_ldap",[{base,Base},
|
||||||
|
{filter, Filter},
|
||||||
|
{attributes, []}]) of
|
||||||
|
#eldap_search_result{entries = E} ->
|
||||||
|
[X || X <- E, ejabberd_auth:is_user_exists(ldap_get_value(X,UIDAttr)) ];
|
||||||
|
_ ->
|
||||||
|
?ERROR_MSG("~p", ["Bad search"])
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
make_filter(Data) ->
|
||||||
|
Filter = [X || X <- lists:map(fun(R) ->
|
||||||
|
make_assertion(R)
|
||||||
|
end,Data),
|
||||||
|
X /= none ],
|
||||||
|
case Filter of
|
||||||
|
[F] ->
|
||||||
|
F;
|
||||||
|
_ ->
|
||||||
|
eldap:'and'(Filter)
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
make_assertion("givenName",Value) ->
|
||||||
|
eldap:substrings("givenName",[{any,Value}]);
|
||||||
|
|
||||||
|
make_assertion("cn",Value) ->
|
||||||
|
eldap:substrings("cn",[{any,Value}]);
|
||||||
|
|
||||||
|
make_assertion("sn",Value) ->
|
||||||
|
eldap:substrings("sn",[{any,Value}]);
|
||||||
|
|
||||||
|
make_assertion(Attr, Value) ->
|
||||||
|
eldap:equalityMatch(Attr,Value).
|
||||||
|
|
||||||
|
make_assertion({SVar, [Val]}) ->
|
||||||
|
LAttr = ldap_attribute(SVar),
|
||||||
|
case LAttr of
|
||||||
|
none ->
|
||||||
|
none;
|
||||||
|
_ ->
|
||||||
|
if
|
||||||
|
is_list(Val) and (Val /= "") ->
|
||||||
|
make_assertion(LAttr,Val);
|
||||||
|
true ->
|
||||||
|
none
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
ldap_attribute("user") ->
|
||||||
|
"uid";
|
||||||
|
|
||||||
|
ldap_attribute("fn") ->
|
||||||
|
"cn";
|
||||||
|
|
||||||
|
ldap_attribute("family") ->
|
||||||
|
"sn";
|
||||||
|
|
||||||
|
ldap_attribute("given") ->
|
||||||
|
"givenName";
|
||||||
|
|
||||||
|
ldap_attribute("middle") ->
|
||||||
|
"initials";
|
||||||
|
|
||||||
|
ldap_attribute("email") ->
|
||||||
|
"mail";
|
||||||
|
|
||||||
|
ldap_attribute("orgname") ->
|
||||||
|
"o";
|
||||||
|
|
||||||
|
ldap_attribute("orgunit") ->
|
||||||
|
"ou";
|
||||||
|
|
||||||
|
ldap_attribute(_) ->
|
||||||
|
none.
|
||||||
|
|
||||||
|
remove_user(User) ->
|
||||||
|
true.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user