mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-04 16:40:36 +01:00
* src/cyrsasl_digest.erl: Bugfix (thanks to Sergei Golovan)
* src/ejabberd.cfg.example: Updated * src/ejabberd_auth.erl: Support for LDAP authentification * src/cyrsasl_digest.erl: Likewise * src/mod_register.erl: Likewise * src/ejabberd_c2s.erl: Likewise * src/eldap/: Imported "eldap" package * src/ejabberd_sm.erl: Bugfix * src/mod_muc/mod_muc_room.erl: Bugfixes SVN Revision: 176
This commit is contained in:
parent
05f988572f
commit
0822a55f05
21
ChangeLog
21
ChangeLog
@ -1,3 +1,24 @@
|
||||
2003-11-23 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/cyrsasl_digest.erl: Bugfix (thanks to Sergei Golovan)
|
||||
|
||||
* src/ejabberd.cfg.example: Updated
|
||||
|
||||
* src/ejabberd_auth.erl: Support for LDAP authentification
|
||||
* src/cyrsasl_digest.erl: Likewise
|
||||
* src/mod_register.erl: Likewise
|
||||
* src/ejabberd_c2s.erl: Likewise
|
||||
|
||||
* src/eldap/: Imported "eldap" package
|
||||
|
||||
* src/ejabberd_sm.erl: Bugfix
|
||||
|
||||
2003-11-16 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Bugfixes
|
||||
|
||||
* (all): Version 0.5 released
|
||||
|
||||
2003-11-13 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* examples/mtr/ejabberd: Updated (thanks to Marshall T. Rose)
|
||||
|
@ -129,6 +129,10 @@ Works on most of popular platforms: *nix (tested on Linux, FreeBSD and
|
||||
<A HREF="http://www.jabber.org/jeps/jep-0060.html">Publish-Subscribe</A>
|
||||
service
|
||||
<LI>Built-in Jabber Users Directory service based on users vCards
|
||||
<LI>SSL support
|
||||
<LI>Ability to interface with external components (JIT, MSN-t, Yahoo-t, etc)
|
||||
<LI>Migration from jabberd14 is possible
|
||||
<LI>Mostly XMPP-compliant
|
||||
<LI>Support for
|
||||
<A HREF="http://www.jabber.org/jeps/jep-0030.html">JEP-0030</A>
|
||||
(Service Discovery).
|
||||
@ -137,6 +141,12 @@ Works on most of popular platforms: *nix (tested on Linux, FreeBSD and
|
||||
(Statistics Gathering).
|
||||
<LI>Support for <TT>xml:lang</TT> attribute in many XML elements
|
||||
</UL>
|
||||
The misfeatures of <TT>ejabberd</TT> is:
|
||||
<UL><LI>
|
||||
No support for external authentification
|
||||
<LI>No support for virtual domains
|
||||
<LI>No support for STARTTLS
|
||||
</UL>
|
||||
<!--TOC section Installation-->
|
||||
|
||||
<H2><A NAME="htoc2">2</A> Installation</H2><!--SEC END -->
|
||||
|
@ -78,6 +78,10 @@ The main features of \ejabberd{} is:
|
||||
\footahref{http://www.jabber.org/jeps/jep-0060.html}{Publish-Subscribe}
|
||||
service
|
||||
\item Built-in Jabber Users Directory service based on users vCards
|
||||
\item SSL support
|
||||
\item Ability to interface with external components (JIT, MSN-t, Yahoo-t, etc)
|
||||
\item Migration from jabberd14 is possible
|
||||
\item Mostly XMPP-compliant
|
||||
\item Support for
|
||||
\footahref{http://www.jabber.org/jeps/jep-0030.html}{JEP-0030}
|
||||
(Service Discovery).
|
||||
@ -87,7 +91,12 @@ The main features of \ejabberd{} is:
|
||||
\item Support for \ns{xml:lang} attribute in many XML elements
|
||||
\end{itemize}
|
||||
|
||||
|
||||
The misfeatures of \ejabberd{} is:
|
||||
\begin{itemize}
|
||||
\item No support for external authentification
|
||||
\item No support for virtual domains
|
||||
\item No support for STARTTLS
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\section{Installation}
|
||||
|
@ -12,7 +12,7 @@ INCLUDES = @ERLANG_CFLAGS@ @EXPAT_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@ @EXPAT_LIBS@
|
||||
|
||||
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ stringprep
|
||||
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ stringprep
|
||||
|
||||
ERLSHLIBS = expat_erl.so
|
||||
|
||||
|
28
src/configure
vendored
28
src/configure
vendored
@ -828,6 +828,7 @@ Optional Features:
|
||||
--enable-mod_pubsub enable mod_pubsub (default: yes)
|
||||
--enable-mod_irc enable mod_irc (default: yes)
|
||||
--enable-mod_muc enable mod_muc (default: yes)
|
||||
--enable-eldap enable eldap (default: yes)
|
||||
|
||||
Optional Packages:
|
||||
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
||||
@ -3675,8 +3676,30 @@ echo "${ECHO_T}$mr_enable_mod_muc" >&6
|
||||
|
||||
|
||||
|
||||
eldap=
|
||||
make_eldap=
|
||||
echo "$as_me:$LINENO: checking whether build eldap" >&5
|
||||
echo $ECHO_N "checking whether build eldap... $ECHO_C" >&6
|
||||
# Check whether --enable-eldap or --disable-eldap was given.
|
||||
if test "${enable_eldap+set}" = set; then
|
||||
enableval="$enable_eldap"
|
||||
mr_enable_eldap="$enableval"
|
||||
else
|
||||
mr_enable_eldap=yes
|
||||
fi;
|
||||
if test "$mr_enable_eldap" = yes; then
|
||||
eldap=eldap
|
||||
make_eldap=eldap/Makefile
|
||||
fi
|
||||
echo "$as_me:$LINENO: result: $mr_enable_eldap" >&5
|
||||
echo "${ECHO_T}$mr_enable_eldap" >&6
|
||||
|
||||
ac_config_files="$ac_config_files Makefile $make_mod_irc $make_mod_muc $make_mod_pubsub stringprep/Makefile"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ac_config_files="$ac_config_files Makefile $make_mod_irc $make_mod_muc $make_mod_pubsub $make_eldap stringprep/Makefile"
|
||||
|
||||
cat >confcache <<\_ACEOF
|
||||
# This file is a shell script that caches the results of configure
|
||||
@ -4178,6 +4201,7 @@ do
|
||||
"$make_mod_irc" ) CONFIG_FILES="$CONFIG_FILES $make_mod_irc" ;;
|
||||
"$make_mod_muc" ) CONFIG_FILES="$CONFIG_FILES $make_mod_muc" ;;
|
||||
"$make_mod_pubsub" ) CONFIG_FILES="$CONFIG_FILES $make_mod_pubsub" ;;
|
||||
"$make_eldap" ) CONFIG_FILES="$CONFIG_FILES $make_eldap" ;;
|
||||
"stringprep/Makefile" ) CONFIG_FILES="$CONFIG_FILES stringprep/Makefile" ;;
|
||||
*) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
|
||||
echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
|
||||
@ -4283,6 +4307,8 @@ s,@mod_irc@,$mod_irc,;t t
|
||||
s,@make_mod_irc@,$make_mod_irc,;t t
|
||||
s,@mod_muc@,$mod_muc,;t t
|
||||
s,@make_mod_muc@,$make_mod_muc,;t t
|
||||
s,@eldap@,$eldap,;t t
|
||||
s,@make_eldap@,$make_eldap,;t t
|
||||
CEOF
|
||||
|
||||
_ACEOF
|
||||
|
@ -29,11 +29,13 @@ AC_HEADER_STDC
|
||||
AC_MOD_ENABLE(mod_pubsub, yes)
|
||||
AC_MOD_ENABLE(mod_irc, yes)
|
||||
AC_MOD_ENABLE(mod_muc, yes)
|
||||
AC_MOD_ENABLE(eldap, yes)
|
||||
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
$make_mod_irc
|
||||
$make_mod_muc
|
||||
$make_mod_pubsub
|
||||
$make_eldap
|
||||
stringprep/Makefile])
|
||||
AC_OUTPUT
|
||||
|
@ -21,8 +21,13 @@
|
||||
-record(state, {step, nonce, username, authzid}).
|
||||
|
||||
start(Opts) ->
|
||||
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE),
|
||||
ok.
|
||||
case ejabberd_auth:plain_password_required() of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE),
|
||||
ok
|
||||
end.
|
||||
|
||||
stop() ->
|
||||
ok.
|
||||
@ -31,7 +36,7 @@ mech_new() ->
|
||||
{ok, #state{step = 1,
|
||||
nonce = randoms:get_string()}}.
|
||||
|
||||
mech_step(#state{step = 1, nonce = Nonce} = State, "") ->
|
||||
mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
|
||||
{continue,
|
||||
"nonce=\"" ++ Nonce ++
|
||||
"\",qop=\"auth\",charset=utf-8,algorithm=md5-sess",
|
||||
|
@ -61,6 +61,17 @@
|
||||
% Admins of this server are also admins of MUC service:
|
||||
{access, muc_admin, [{allow, admin}]}.
|
||||
|
||||
|
||||
% Authentification method. If you want to use internal user base, then use
|
||||
% this line:
|
||||
{auth_method, internal}.
|
||||
|
||||
% For LDAP uthentification use these lines instead of above one:
|
||||
%{auth_method, ldap}.
|
||||
%{ldap_servers, ["localhost"]}. % List of LDAP servers
|
||||
%{ldap_base, "dc=example,dc=com"}. % Base of LDAP directory
|
||||
|
||||
|
||||
% Host name:
|
||||
{host, "localhost"}.
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(VERSION, "0.1-alpha").
|
||||
-define(VERSION, "0.6-alpha").
|
||||
|
||||
%-define(ejabberd_debug, true).
|
||||
%-define(DBGFSM, true).
|
||||
|
@ -23,7 +23,11 @@
|
||||
get_password_s/1,
|
||||
is_user_exists/1,
|
||||
remove_user/1,
|
||||
remove_user/2]).
|
||||
remove_user/2,
|
||||
plain_password_required/0,
|
||||
check_password_ldap/2, % TODO: remove
|
||||
is_user_exists_ldap/1 % TODO: remove
|
||||
]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
@ -33,6 +37,8 @@
|
||||
handle_info/2,
|
||||
terminate/2]).
|
||||
|
||||
-include("eldap/eldap.hrl").
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
-record(passwd, {user, password}).
|
||||
@ -59,6 +65,13 @@ start_link() ->
|
||||
init([]) ->
|
||||
mnesia:create_table(passwd,[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, passwd)}]),
|
||||
case auth_method() of
|
||||
internal ->
|
||||
ok;
|
||||
ldap ->
|
||||
LDAPServers = ejabberd_config:get_local_option(ldap_servers),
|
||||
eldap:start_link("ejabberd", LDAPServers, 389, "", "")
|
||||
end,
|
||||
{ok, #state{}}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
@ -108,7 +121,32 @@ terminate(_Reason, _State) ->
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
auth_method() ->
|
||||
case ejabberd_config:get_local_option(auth_method) of
|
||||
ldap ->
|
||||
ldap;
|
||||
_ ->
|
||||
internal
|
||||
end.
|
||||
|
||||
plain_password_required() ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
false;
|
||||
ldap ->
|
||||
true
|
||||
end.
|
||||
|
||||
|
||||
check_password(User, Password) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
check_password_internal(User, Password);
|
||||
ldap ->
|
||||
check_password_ldap(User, Password)
|
||||
end.
|
||||
|
||||
check_password_internal(User, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
[#passwd{password = Password}] ->
|
||||
@ -118,6 +156,14 @@ check_password(User, Password) ->
|
||||
end.
|
||||
|
||||
check_password(User, Password, StreamID, Digest) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
check_password_internal(User, Password, StreamID, Digest);
|
||||
ldap ->
|
||||
check_password_ldap(User, Password, StreamID, Digest)
|
||||
end.
|
||||
|
||||
check_password_internal(User, Password, StreamID, Digest) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
[#passwd{password = Passwd}] ->
|
||||
@ -148,7 +194,16 @@ set_password(User, Password) ->
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
|
||||
try_register(User, Password) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
try_register_internal(User, Password);
|
||||
ldap ->
|
||||
{error, not_allowed}
|
||||
end.
|
||||
|
||||
try_register_internal(User, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error -> {error, invalid_jid};
|
||||
LUser ->
|
||||
@ -187,6 +242,14 @@ get_password_s(User) ->
|
||||
end.
|
||||
|
||||
is_user_exists(User) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
is_user_exists_internal(User);
|
||||
ldap ->
|
||||
is_user_exists_ldap(User)
|
||||
end.
|
||||
|
||||
is_user_exists_internal(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
case catch mnesia:dirty_read({passwd, LUser}) of
|
||||
[] ->
|
||||
@ -198,6 +261,14 @@ is_user_exists(User) ->
|
||||
end.
|
||||
|
||||
remove_user(User) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
remove_user_internal(User);
|
||||
ldap ->
|
||||
{error, not_allowed}
|
||||
end.
|
||||
|
||||
remove_user_internal(User) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
F = fun() ->
|
||||
mnesia:delete({passwd, LUser})
|
||||
@ -210,6 +281,14 @@ remove_user(User) ->
|
||||
catch mod_private:remove_user(User).
|
||||
|
||||
remove_user(User, Password) ->
|
||||
case auth_method() of
|
||||
internal ->
|
||||
remove_user_internal(User, Password);
|
||||
ldap ->
|
||||
not_allowed
|
||||
end.
|
||||
|
||||
remove_user_internal(User, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
F = fun() ->
|
||||
case mnesia:read({passwd, LUser}) of
|
||||
@ -236,3 +315,44 @@ remove_user(User, Password) ->
|
||||
bad_request
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
check_password_ldap(User, Password, StreamID, Digest) ->
|
||||
check_password_ldap(User, Password).
|
||||
|
||||
check_password_ldap(User, Password) ->
|
||||
case find_user_dn(User) of
|
||||
false ->
|
||||
false;
|
||||
DN ->
|
||||
case eldap:bind("ejabberd", DN, Password) of
|
||||
ok ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
is_user_exists_ldap(User) ->
|
||||
case find_user_dn(User) of
|
||||
false ->
|
||||
false;
|
||||
_DN ->
|
||||
true
|
||||
end.
|
||||
|
||||
find_user_dn(User) ->
|
||||
Filter = eldap:equalityMatch("uid", User),
|
||||
Base = ejabberd_config:get_local_option(ldap_base),
|
||||
case eldap:search("ejabberd", [{base, Base},
|
||||
{filter, Filter},
|
||||
{attributes, []}]) of
|
||||
#eldap_search_result{entries = [E | _]} ->
|
||||
E#eldap_entry.object_name;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
@ -201,13 +201,22 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
{next_state, wait_for_auth, StateData};
|
||||
{auth, ID, get, {U, _, _, _}} ->
|
||||
{xmlelement, Name, Attrs, Els} = jlib:make_result_iq_reply(El),
|
||||
Res = {xmlelement, Name, Attrs,
|
||||
[{xmlelement, "query", [{"xmlns", ?NS_AUTH}],
|
||||
[{xmlelement, "username", [], [{xmlcdata, U}]},
|
||||
{xmlelement, "password", [], []},
|
||||
{xmlelement, "digest", [], []},
|
||||
{xmlelement, "resource", [], []}
|
||||
]}]},
|
||||
Res = case ejabberd_auth:plain_password_required() of
|
||||
false ->
|
||||
{xmlelement, Name, Attrs,
|
||||
[{xmlelement, "query", [{"xmlns", ?NS_AUTH}],
|
||||
[{xmlelement, "username", [], [{xmlcdata, U}]},
|
||||
{xmlelement, "password", [], []},
|
||||
{xmlelement, "digest", [], []},
|
||||
{xmlelement, "resource", [], []}
|
||||
]}]};
|
||||
true ->
|
||||
{xmlelement, Name, Attrs,
|
||||
[{xmlelement, "query", [{"xmlns", ?NS_AUTH}],
|
||||
[{xmlelement, "username", [], [{xmlcdata, U}]},
|
||||
{xmlelement, "password", [], []}
|
||||
]}]}
|
||||
end,
|
||||
send_element(StateData, Res),
|
||||
{next_state, wait_for_auth, StateData};
|
||||
{auth, ID, set, {U, P, D, ""}} ->
|
||||
|
@ -298,12 +298,12 @@ route_message(From, To, Packet) ->
|
||||
#jid{luser = LUser} = To,
|
||||
case catch lists:max(get_user_present_resources(LUser)) of
|
||||
{'EXIT', _} ->
|
||||
case ejabberd_auth:is_user_exists(LUser) of
|
||||
true ->
|
||||
case xml:get_tag_attr_s("type", Packet) of
|
||||
"error" ->
|
||||
ok;
|
||||
_ ->
|
||||
case xml:get_tag_attr_s("type", Packet) of
|
||||
"error" ->
|
||||
ok;
|
||||
_ ->
|
||||
case ejabberd_auth:is_user_exists(LUser) of
|
||||
true ->
|
||||
case catch mod_offline:store_packet(
|
||||
From, To, Packet) of
|
||||
{'EXIT', _} ->
|
||||
@ -312,12 +312,12 @@ route_message(From, To, Packet) ->
|
||||
ejabberd_router:route(To, From, Err);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_ITEM_NOT_FOUND),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_ITEM_NOT_FOUND),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end
|
||||
end;
|
||||
{_, R} ->
|
||||
ejabberd_sm ! {route,
|
||||
|
291
src/eldap/ELDAPv3.asn
Normal file
291
src/eldap/ELDAPv3.asn
Normal file
@ -0,0 +1,291 @@
|
||||
-- LDAPv3 ASN.1 specification, taken from RFC 2251
|
||||
|
||||
-- Lightweight-Directory-Access-Protocol-V3 DEFINITIONS
|
||||
ELDAPv3 DEFINITIONS
|
||||
IMPLICIT TAGS ::=
|
||||
|
||||
BEGIN
|
||||
|
||||
LDAPMessage ::= SEQUENCE {
|
||||
messageID MessageID,
|
||||
protocolOp CHOICE {
|
||||
bindRequest BindRequest,
|
||||
bindResponse BindResponse,
|
||||
unbindRequest UnbindRequest,
|
||||
searchRequest SearchRequest,
|
||||
searchResEntry SearchResultEntry,
|
||||
searchResDone SearchResultDone,
|
||||
searchResRef SearchResultReference,
|
||||
modifyRequest ModifyRequest,
|
||||
modifyResponse ModifyResponse,
|
||||
addRequest AddRequest,
|
||||
addResponse AddResponse,
|
||||
delRequest DelRequest,
|
||||
delResponse DelResponse,
|
||||
modDNRequest ModifyDNRequest,
|
||||
modDNResponse ModifyDNResponse,
|
||||
compareRequest CompareRequest,
|
||||
compareResponse CompareResponse,
|
||||
abandonRequest AbandonRequest,
|
||||
extendedReq ExtendedRequest,
|
||||
extendedResp ExtendedResponse },
|
||||
controls [0] Controls OPTIONAL }
|
||||
|
||||
MessageID ::= INTEGER (0 .. maxInt)
|
||||
|
||||
maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
|
||||
|
||||
LDAPString ::= OCTET STRING
|
||||
|
||||
LDAPOID ::= OCTET STRING
|
||||
|
||||
LDAPDN ::= LDAPString
|
||||
|
||||
RelativeLDAPDN ::= LDAPString
|
||||
|
||||
AttributeType ::= LDAPString
|
||||
|
||||
AttributeDescription ::= LDAPString
|
||||
|
||||
|
||||
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 44]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
|
||||
AttributeDescriptionList ::= SEQUENCE OF
|
||||
AttributeDescription
|
||||
|
||||
AttributeValue ::= OCTET STRING
|
||||
|
||||
AttributeValueAssertion ::= SEQUENCE {
|
||||
attributeDesc AttributeDescription,
|
||||
assertionValue AssertionValue }
|
||||
|
||||
AssertionValue ::= OCTET STRING
|
||||
|
||||
Attribute ::= SEQUENCE {
|
||||
type AttributeDescription,
|
||||
vals SET OF AttributeValue }
|
||||
|
||||
MatchingRuleId ::= LDAPString
|
||||
|
||||
LDAPResult ::= SEQUENCE {
|
||||
resultCode ENUMERATED {
|
||||
success (0),
|
||||
operationsError (1),
|
||||
protocolError (2),
|
||||
timeLimitExceeded (3),
|
||||
sizeLimitExceeded (4),
|
||||
compareFalse (5),
|
||||
compareTrue (6),
|
||||
authMethodNotSupported (7),
|
||||
strongAuthRequired (8),
|
||||
-- 9 reserved --
|
||||
referral (10), -- new
|
||||
adminLimitExceeded (11), -- new
|
||||
unavailableCriticalExtension (12), -- new
|
||||
confidentialityRequired (13), -- new
|
||||
saslBindInProgress (14), -- new
|
||||
noSuchAttribute (16),
|
||||
undefinedAttributeType (17),
|
||||
inappropriateMatching (18),
|
||||
constraintViolation (19),
|
||||
attributeOrValueExists (20),
|
||||
invalidAttributeSyntax (21),
|
||||
-- 22-31 unused --
|
||||
noSuchObject (32),
|
||||
aliasProblem (33),
|
||||
invalidDNSyntax (34),
|
||||
-- 35 reserved for undefined isLeaf --
|
||||
aliasDereferencingProblem (36),
|
||||
-- 37-47 unused --
|
||||
inappropriateAuthentication (48),
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 45]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
|
||||
invalidCredentials (49),
|
||||
insufficientAccessRights (50),
|
||||
busy (51),
|
||||
unavailable (52),
|
||||
unwillingToPerform (53),
|
||||
loopDetect (54),
|
||||
-- 55-63 unused --
|
||||
namingViolation (64),
|
||||
objectClassViolation (65),
|
||||
notAllowedOnNonLeaf (66),
|
||||
notAllowedOnRDN (67),
|
||||
entryAlreadyExists (68),
|
||||
objectClassModsProhibited (69),
|
||||
-- 70 reserved for CLDAP --
|
||||
affectsMultipleDSAs (71), -- new
|
||||
-- 72-79 unused --
|
||||
other (80) },
|
||||
-- 81-90 reserved for APIs --
|
||||
matchedDN LDAPDN,
|
||||
errorMessage LDAPString,
|
||||
referral [3] Referral OPTIONAL }
|
||||
|
||||
Referral ::= SEQUENCE OF LDAPURL
|
||||
|
||||
LDAPURL ::= LDAPString -- limited to characters permitted in URLs
|
||||
|
||||
Controls ::= SEQUENCE OF Control
|
||||
|
||||
Control ::= SEQUENCE {
|
||||
controlType LDAPOID,
|
||||
criticality BOOLEAN DEFAULT FALSE,
|
||||
controlValue OCTET STRING OPTIONAL }
|
||||
|
||||
BindRequest ::= [APPLICATION 0] SEQUENCE {
|
||||
version INTEGER (1 .. 127),
|
||||
name LDAPDN,
|
||||
authentication AuthenticationChoice }
|
||||
|
||||
AuthenticationChoice ::= CHOICE {
|
||||
simple [0] OCTET STRING,
|
||||
-- 1 and 2 reserved
|
||||
sasl [3] SaslCredentials }
|
||||
|
||||
SaslCredentials ::= SEQUENCE {
|
||||
mechanism LDAPString,
|
||||
credentials OCTET STRING OPTIONAL }
|
||||
|
||||
BindResponse ::= [APPLICATION 1] SEQUENCE {
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 46]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
|
||||
COMPONENTS OF LDAPResult,
|
||||
serverSaslCreds [7] OCTET STRING OPTIONAL }
|
||||
|
||||
UnbindRequest ::= [APPLICATION 2] NULL
|
||||
|
||||
SearchRequest ::= [APPLICATION 3] SEQUENCE {
|
||||
baseObject LDAPDN,
|
||||
scope ENUMERATED {
|
||||
baseObject (0),
|
||||
singleLevel (1),
|
||||
wholeSubtree (2) },
|
||||
derefAliases ENUMERATED {
|
||||
neverDerefAliases (0),
|
||||
derefInSearching (1),
|
||||
derefFindingBaseObj (2),
|
||||
derefAlways (3) },
|
||||
sizeLimit INTEGER (0 .. maxInt),
|
||||
timeLimit INTEGER (0 .. maxInt),
|
||||
typesOnly BOOLEAN,
|
||||
filter Filter,
|
||||
attributes AttributeDescriptionList }
|
||||
|
||||
Filter ::= CHOICE {
|
||||
and [0] SET OF Filter,
|
||||
or [1] SET OF Filter,
|
||||
not [2] Filter,
|
||||
equalityMatch [3] AttributeValueAssertion,
|
||||
substrings [4] SubstringFilter,
|
||||
greaterOrEqual [5] AttributeValueAssertion,
|
||||
lessOrEqual [6] AttributeValueAssertion,
|
||||
present [7] AttributeDescription,
|
||||
approxMatch [8] AttributeValueAssertion,
|
||||
extensibleMatch [9] MatchingRuleAssertion }
|
||||
|
||||
SubstringFilter ::= SEQUENCE {
|
||||
type AttributeDescription,
|
||||
-- at least one must be present
|
||||
substrings SEQUENCE OF CHOICE {
|
||||
initial [0] LDAPString,
|
||||
any [1] LDAPString,
|
||||
final [2] LDAPString } }
|
||||
|
||||
MatchingRuleAssertion ::= SEQUENCE {
|
||||
matchingRule [1] MatchingRuleId OPTIONAL,
|
||||
type [2] AttributeDescription OPTIONAL,
|
||||
matchValue [3] AssertionValue,
|
||||
dnAttributes [4] BOOLEAN DEFAULT FALSE }
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 47]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
|
||||
objectName LDAPDN,
|
||||
attributes PartialAttributeList }
|
||||
|
||||
PartialAttributeList ::= SEQUENCE OF SEQUENCE {
|
||||
type AttributeDescription,
|
||||
vals SET OF AttributeValue }
|
||||
|
||||
SearchResultReference ::= [APPLICATION 19] SEQUENCE OF LDAPURL
|
||||
|
||||
SearchResultDone ::= [APPLICATION 5] LDAPResult
|
||||
|
||||
ModifyRequest ::= [APPLICATION 6] SEQUENCE {
|
||||
object LDAPDN,
|
||||
modification SEQUENCE OF SEQUENCE {
|
||||
operation ENUMERATED {
|
||||
add (0),
|
||||
delete (1),
|
||||
replace (2) },
|
||||
modification AttributeTypeAndValues } }
|
||||
|
||||
AttributeTypeAndValues ::= SEQUENCE {
|
||||
type AttributeDescription,
|
||||
vals SET OF AttributeValue }
|
||||
|
||||
ModifyResponse ::= [APPLICATION 7] LDAPResult
|
||||
|
||||
AddRequest ::= [APPLICATION 8] SEQUENCE {
|
||||
entry LDAPDN,
|
||||
attributes AttributeList }
|
||||
|
||||
AttributeList ::= SEQUENCE OF SEQUENCE {
|
||||
type AttributeDescription,
|
||||
vals SET OF AttributeValue }
|
||||
|
||||
AddResponse ::= [APPLICATION 9] LDAPResult
|
||||
|
||||
DelRequest ::= [APPLICATION 10] LDAPDN
|
||||
|
||||
DelResponse ::= [APPLICATION 11] LDAPResult
|
||||
|
||||
ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
|
||||
entry LDAPDN,
|
||||
newrdn RelativeLDAPDN,
|
||||
deleteoldrdn BOOLEAN,
|
||||
newSuperior [0] LDAPDN OPTIONAL }
|
||||
|
||||
ModifyDNResponse ::= [APPLICATION 13] LDAPResult
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 48]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
|
||||
CompareRequest ::= [APPLICATION 14] SEQUENCE {
|
||||
entry LDAPDN,
|
||||
ava AttributeValueAssertion }
|
||||
|
||||
CompareResponse ::= [APPLICATION 15] LDAPResult
|
||||
|
||||
AbandonRequest ::= [APPLICATION 16] MessageID
|
||||
|
||||
ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
|
||||
requestName [0] LDAPOID,
|
||||
requestValue [1] OCTET STRING OPTIONAL }
|
||||
|
||||
ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
|
||||
COMPONENTS OF LDAPResult,
|
||||
responseName [10] LDAPOID OPTIONAL,
|
||||
response [11] OCTET STRING OPTIONAL }
|
||||
|
||||
END
|
||||
|
||||
|
36
src/eldap/Makefile.in
Normal file
36
src/eldap/Makefile.in
Normal file
@ -0,0 +1,36 @@
|
||||
# $Id$
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
|
||||
INCLUDES = @ERLANG_CFLAGS@
|
||||
|
||||
LIBDIRS = @ERLANG_LIBS@
|
||||
|
||||
SUBDIRS =
|
||||
|
||||
|
||||
OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
OBJS = \
|
||||
$(OUTDIR)/eldap.beam \
|
||||
$(OUTDIR)/ELDAPv3.beam
|
||||
|
||||
all: $(OBJS)
|
||||
|
||||
ELDAPv3.erl: ELDAPv3.asn
|
||||
erlc -bber_bin -W $(EFLAGS) $<
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl ELDAPv3.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS)
|
||||
|
||||
TAGS:
|
||||
etags *.erl
|
||||
|
995
src/eldap/eldap.erl
Normal file
995
src/eldap/eldap.erl
Normal file
@ -0,0 +1,995 @@
|
||||
-module(eldap).
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Created: 12 Oct 2000 by Tobbe <tnt@home.se>
|
||||
%%% Function: Erlang client LDAP implementation according RFC 2251.
|
||||
%%% The interface is based on RFC 1823, and
|
||||
%%% draft-ietf-asid-ldap-c-api-00.txt
|
||||
%%%
|
||||
%%% Copyright (C) 2000 Torbjörn Törnkvist, tnt@home.se
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or modify
|
||||
%%% it under the terms of the GNU General Public License as published by
|
||||
%%% the Free Software Foundation; either version 2 of the License, or
|
||||
%%% (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%%% GNU General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
%%% Modified by Sean Hinde <shinde@iee.org> 7th Dec 2000
|
||||
%%% Turned into gen_fsm, made non-blocking, added timers etc to support this.
|
||||
%%% Now has the concept of a name (string() or atom()) per instance which allows
|
||||
%%% multiple users to call by name if so desired.
|
||||
%%%
|
||||
%%% Can be configured with start_link parameters or use a config file to get
|
||||
%%% host to connect to, dn, password, log function etc.
|
||||
|
||||
|
||||
%%% Modified by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% --------------------------------------------------------------------
|
||||
-vc('$Id$ ').
|
||||
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% LDAP Client state machine.
|
||||
%%% Possible states are:
|
||||
%%% connecting - actually disconnected, but retrying periodically
|
||||
%%% wait_bind_response - connected and sent bind request
|
||||
%%% active - bound to LDAP Server and ready to handle commands
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%-compile(export_all).
|
||||
%%-export([Function/Arity, ...]).
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% External exports
|
||||
-export([start_link/1, start_link/5, start_link/6]).
|
||||
|
||||
-export([baseObject/0,singleLevel/0,wholeSubtree/0,close/1,
|
||||
equalityMatch/2,greaterOrEqual/2,lessOrEqual/2,
|
||||
approxMatch/2,search/2,substrings/2,present/1,
|
||||
'and'/1,'or'/1,'not'/1,modify/3, mod_add/2, mod_delete/2,
|
||||
mod_replace/2, add/3, delete/2, modify_dn/5, bind/3]).
|
||||
-export([debug_level/2, get_status/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1, connecting/2,
|
||||
connecting/3, wait_bind_response/3, active/3, handle_event/3,
|
||||
handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
|
||||
|
||||
|
||||
-import(lists,[concat/1]).
|
||||
|
||||
-include("ELDAPv3.hrl").
|
||||
-include("eldap.hrl").
|
||||
|
||||
-define(LDAP_VERSION, 3).
|
||||
-define(RETRY_TIMEOUT, 5000).
|
||||
-define(BIND_TIMEOUT, 10000).
|
||||
-define(CMD_TIMEOUT, 5000).
|
||||
-define(MAX_TRANSACTION_ID, 65535).
|
||||
-define(MIN_TRANSACTION_ID, 0).
|
||||
|
||||
-record(eldap, {version = ?LDAP_VERSION,
|
||||
hosts, % Possible hosts running LDAP servers
|
||||
host = null, % Connected Host LDAP server
|
||||
port = 389 , % The LDAP server port
|
||||
fd = null, % Socket filedescriptor.
|
||||
rootdn = "", % Name of the entry to bind as
|
||||
passwd, % Password for (above) entry
|
||||
id = 0, % LDAP Request ID
|
||||
log, % User provided log function
|
||||
bind_timer, % Ref to bind timeout
|
||||
dict, % dict holding operation params and results
|
||||
debug_level % Integer debug/logging level
|
||||
}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start_link(Name) ->
|
||||
Reg_name = list_to_atom("eldap_" ++ Name),
|
||||
gen_fsm:start_link({local, Reg_name}, ?MODULE, [], []).
|
||||
|
||||
start_link(Name, Hosts, Port, Rootdn, Passwd) ->
|
||||
Log = fun(N, Fmt, Args) -> io:format("---- " ++ Fmt, [Args]) end,
|
||||
Reg_name = list_to_atom("eldap_" ++ Name),
|
||||
gen_fsm:start_link({local, Reg_name}, ?MODULE, {Hosts, Port, Rootdn, Passwd, Log}, []).
|
||||
|
||||
start_link(Name, Hosts, Port, Rootdn, Passwd, Log) ->
|
||||
Reg_name = list_to_atom("eldap_" ++ Name),
|
||||
gen_fsm:start_link({local, Reg_name}, ?MODULE, {Hosts, Port, Rootdn, Passwd, Log}, []).
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Set Debug Level. 0 - none, 1 - errors, 2 - ldap events
|
||||
%%% --------------------------------------------------------------------
|
||||
debug_level(Handle, N) when integer(N) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_all_state_event(Handle1, {debug_level,N}).
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Get status of connection.
|
||||
%%% --------------------------------------------------------------------
|
||||
get_status(Handle) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_all_state_event(Handle1, get_status).
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Shutdown connection (and process) asynchronous.
|
||||
%%% --------------------------------------------------------------------
|
||||
close(Handle) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:send_all_state_event(Handle1, close).
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Add an entry. The entry field MUST NOT exist for the AddRequest
|
||||
%%% to succeed. The parent of the entry MUST exist.
|
||||
%%% Example:
|
||||
%%%
|
||||
%%% add(Handle,
|
||||
%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com",
|
||||
%%% [{"objectclass", ["person"]},
|
||||
%%% {"cn", ["Bill Valentine"]},
|
||||
%%% {"sn", ["Valentine"]},
|
||||
%%% {"telephoneNumber", ["545 555 00"]}]
|
||||
%%% )
|
||||
%%% --------------------------------------------------------------------
|
||||
add(Handle, Entry, Attributes) when list(Entry),list(Attributes) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(Handle1, {add, Entry, add_attrs(Attributes)}).
|
||||
|
||||
%%% Do sanity check !
|
||||
add_attrs(Attrs) ->
|
||||
F = fun({Type,Vals}) when list(Type),list(Vals) ->
|
||||
%% Confused ? Me too... :-/
|
||||
{'AddRequest_attributes',Type, Vals}
|
||||
end,
|
||||
case catch lists:map(F, Attrs) of
|
||||
{'EXIT', _} -> throw({error, attribute_values});
|
||||
Else -> Else
|
||||
end.
|
||||
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Delete an entry. The entry consists of the DN of
|
||||
%%% the entry to be deleted.
|
||||
%%% Example:
|
||||
%%%
|
||||
%%% delete(Handle,
|
||||
%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com"
|
||||
%%% )
|
||||
%%% --------------------------------------------------------------------
|
||||
delete(Handle, Entry) when list(Entry) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(Handle1, {delete, Entry}).
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Modify an entry. Given an entry a number of modification
|
||||
%%% operations can be performed as one atomic operation.
|
||||
%%% Example:
|
||||
%%%
|
||||
%%% modify(Handle,
|
||||
%%% "cn=Torbjorn Tornkvist, ou=people, o=Bluetail AB, dc=bluetail, dc=com",
|
||||
%%% [replace("telephoneNumber", ["555 555 00"]),
|
||||
%%% add("description", ["LDAP hacker"])]
|
||||
%%% )
|
||||
%%% --------------------------------------------------------------------
|
||||
modify(Handle, Object, Mods) when list(Object), list(Mods) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(Handle1, {modify, Object, Mods}).
|
||||
|
||||
%%%
|
||||
%%% Modification operations.
|
||||
%%% Example:
|
||||
%%% replace("telephoneNumber", ["555 555 00"])
|
||||
%%%
|
||||
mod_add(Type, Values) when list(Type), list(Values) -> m(add, Type, Values).
|
||||
mod_delete(Type, Values) when list(Type), list(Values) -> m(delete, Type, Values).
|
||||
mod_replace(Type, Values) when list(Type), list(Values) -> m(replace, Type, Values).
|
||||
|
||||
m(Operation, Type, Values) ->
|
||||
#'ModifyRequest_modification_SEQOF'{
|
||||
operation = Operation,
|
||||
modification = #'AttributeTypeAndValues'{
|
||||
type = Type,
|
||||
vals = Values}}.
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Modify an entry. Given an entry a number of modification
|
||||
%%% operations can be performed as one atomic operation.
|
||||
%%% Example:
|
||||
%%%
|
||||
%%% modify_dn(Handle,
|
||||
%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com",
|
||||
%%% "cn=Ben Emerson",
|
||||
%%% true,
|
||||
%%% ""
|
||||
%%% )
|
||||
%%% --------------------------------------------------------------------
|
||||
modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup)
|
||||
when list(Entry),list(NewRDN),atom(DelOldRDN),list(NewSup) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(Handle1, {modify_dn, Entry, NewRDN, bool_p(DelOldRDN), optional(NewSup)}).
|
||||
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Bind.
|
||||
%%% Example:
|
||||
%%%
|
||||
%%% bind(Handle,
|
||||
%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com",
|
||||
%%% "secret")
|
||||
%%% --------------------------------------------------------------------
|
||||
bind(Handle, RootDN, Passwd)
|
||||
when list(RootDN),list(Passwd) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(Handle1, {bind, RootDN, Passwd}).
|
||||
|
||||
%%% Sanity checks !
|
||||
|
||||
bool_p(Bool) when Bool==true;Bool==false -> Bool.
|
||||
|
||||
optional([]) -> asn1_NOVALUE;
|
||||
optional(Value) -> Value.
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Synchronous search of the Directory returning a
|
||||
%%% requested set of attributes.
|
||||
%%%
|
||||
%%% Example:
|
||||
%%%
|
||||
%%% Filter = eldap:substrings("sn", [{any,"o"}]),
|
||||
%%% eldap:search(S, [{base, "dc=bluetail, dc=com"},
|
||||
%%% {filter, Filter},
|
||||
%%% {attributes,["cn"]}])),
|
||||
%%%
|
||||
%%% Returned result: {ok, #eldap_search_result{}}
|
||||
%%%
|
||||
%%% Example:
|
||||
%%%
|
||||
%%% {ok,{eldap_search_result,
|
||||
%%% [{eldap_entry,
|
||||
%%% "cn=Magnus Froberg, dc=bluetail, dc=com",
|
||||
%%% [{"cn",["Magnus Froberg"]}]},
|
||||
%%% {eldap_entry,
|
||||
%%% "cn=Torbjorn Tornkvist, dc=bluetail, dc=com",
|
||||
%%% [{"cn",["Torbjorn Tornkvist"]}]}],
|
||||
%%% []}}
|
||||
%%%
|
||||
%%% --------------------------------------------------------------------
|
||||
search(Handle, A) when record(A, eldap_search) ->
|
||||
call_search(Handle, A);
|
||||
search(Handle, L) when list(Handle), list(L) ->
|
||||
case catch parse_search_args(L) of
|
||||
{error, Emsg} -> {error, Emsg};
|
||||
{'EXIT', Emsg} -> {error, Emsg};
|
||||
A when record(A, eldap_search) -> call_search(Handle, A)
|
||||
end.
|
||||
|
||||
call_search(Handle, A) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(Handle1, {search, A}).
|
||||
|
||||
parse_search_args(Args) ->
|
||||
parse_search_args(Args, #eldap_search{scope = wholeSubtree}).
|
||||
|
||||
parse_search_args([{base, Base}|T],A) ->
|
||||
parse_search_args(T,A#eldap_search{base = Base});
|
||||
parse_search_args([{filter, Filter}|T],A) ->
|
||||
parse_search_args(T,A#eldap_search{filter = Filter});
|
||||
parse_search_args([{scope, Scope}|T],A) ->
|
||||
parse_search_args(T,A#eldap_search{scope = Scope});
|
||||
parse_search_args([{attributes, Attrs}|T],A) ->
|
||||
parse_search_args(T,A#eldap_search{attributes = Attrs});
|
||||
parse_search_args([{types_only, TypesOnly}|T],A) ->
|
||||
parse_search_args(T,A#eldap_search{types_only = TypesOnly});
|
||||
parse_search_args([{timeout, Timeout}|T],A) when integer(Timeout) ->
|
||||
parse_search_args(T,A#eldap_search{timeout = Timeout});
|
||||
parse_search_args([H|T],A) ->
|
||||
throw({error,{unknown_arg, H}});
|
||||
parse_search_args([],A) ->
|
||||
A.
|
||||
|
||||
%%%
|
||||
%%% The Scope parameter
|
||||
%%%
|
||||
baseObject() -> baseObject.
|
||||
singleLevel() -> singleLevel.
|
||||
wholeSubtree() -> wholeSubtree.
|
||||
|
||||
%%%
|
||||
%%% Boolean filter operations
|
||||
%%%
|
||||
'and'(ListOfFilters) when list(ListOfFilters) -> {'and',ListOfFilters}.
|
||||
'or'(ListOfFilters) when list(ListOfFilters) -> {'or', ListOfFilters}.
|
||||
'not'(Filter) when tuple(Filter) -> {'not',Filter}.
|
||||
|
||||
%%%
|
||||
%%% The following Filter parameters consist of an attribute
|
||||
%%% and an attribute value. Example: F("uid","tobbe")
|
||||
%%%
|
||||
equalityMatch(Desc, Value) -> {equalityMatch, av_assert(Desc, Value)}.
|
||||
greaterOrEqual(Desc, Value) -> {greaterOrEqual, av_assert(Desc, Value)}.
|
||||
lessOrEqual(Desc, Value) -> {lessOrEqual, av_assert(Desc, Value)}.
|
||||
approxMatch(Desc, Value) -> {approxMatch, av_assert(Desc, Value)}.
|
||||
|
||||
av_assert(Desc, Value) ->
|
||||
#'AttributeValueAssertion'{attributeDesc = Desc,
|
||||
assertionValue = Value}.
|
||||
|
||||
%%%
|
||||
%%% Filter to check for the presence of an attribute
|
||||
%%%
|
||||
present(Attribute) when list(Attribute) ->
|
||||
{present, Attribute}.
|
||||
|
||||
|
||||
%%%
|
||||
%%% A substring filter seem to be based on a pattern:
|
||||
%%%
|
||||
%%% InitValue*AnyValue*FinalValue
|
||||
%%%
|
||||
%%% where all three parts seem to be optional (at least when
|
||||
%%% talking with an OpenLDAP server). Thus, the arguments
|
||||
%%% to substrings/2 looks like this:
|
||||
%%%
|
||||
%%% Type ::= string( <attribute> )
|
||||
%%% SubStr ::= listof( {initial,Value} | {any,Value}, {final,Value})
|
||||
%%%
|
||||
%%% Example: substrings("sn",[{initial,"To"},{any,"kv"},{final,"st"}])
|
||||
%%% will match entries containing: 'sn: Tornkvist'
|
||||
%%%
|
||||
substrings(Type, SubStr) when list(Type), list(SubStr) ->
|
||||
Ss = {'SubstringFilter_substrings',v_substr(SubStr)},
|
||||
{substrings,#'SubstringFilter'{type = Type,
|
||||
substrings = Ss}}.
|
||||
|
||||
|
||||
get_handle(Pid) when pid(Pid) -> Pid;
|
||||
get_handle(Atom) when atom(Atom) -> Atom;
|
||||
get_handle(Name) when list(Name) -> list_to_atom("eldap_" ++ Name).
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: init/1
|
||||
%% Returns: {ok, StateName, StateData} |
|
||||
%% {ok, StateName, StateData, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, StopReason}
|
||||
%% I use the trick of setting a timeout of 0 to pass control into the
|
||||
%% process.
|
||||
%%----------------------------------------------------------------------
|
||||
init([]) ->
|
||||
case get_config() of
|
||||
{ok, Hosts, Rootdn, Passwd, Log} ->
|
||||
init({Hosts, Rootdn, Passwd, Log});
|
||||
{error, Reason} ->
|
||||
{stop, Reason}
|
||||
end;
|
||||
init({Hosts, Port, Rootdn, Passwd, Log}) ->
|
||||
{ok, connecting, #eldap{hosts = Hosts,
|
||||
port = Port,
|
||||
rootdn = Rootdn,
|
||||
passwd = Passwd,
|
||||
id = 0,
|
||||
log = Log,
|
||||
dict = dict:new(),
|
||||
debug_level = 0}, 0}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
%% Called when gen_fsm:send_event/2,3 is invoked (async)
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
connecting(timeout, S) ->
|
||||
{ok, NextState, NewS} = connect_bind(S),
|
||||
{next_state, NextState, NewS}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/3
|
||||
%% Called when gen_fsm:sync_send_event/2,3 is invoked.
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
connecting(Event, From, S) ->
|
||||
Reply = {error, connecting},
|
||||
{reply, Reply, connecting, S}.
|
||||
|
||||
wait_bind_response(Event, From, S) ->
|
||||
Reply = {error, wait_bind_response},
|
||||
{reply, Reply, wait_bind_response, S}.
|
||||
|
||||
active(Event, From, S) ->
|
||||
case catch send_command(Event, From, S) of
|
||||
{ok, NewS} ->
|
||||
{next_state, active, NewS};
|
||||
{error, Reason} ->
|
||||
{reply, {error, Reason}, active, S};
|
||||
{'EXIT', Reason} ->
|
||||
{reply, {error, Reason}, active, S}
|
||||
end.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_event/3
|
||||
%% Called when gen_fsm:send_all_state_event/2 is invoked.
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_event(close, StateName, S) ->
|
||||
gen_tcp:close(S#eldap.fd),
|
||||
{stop, closed, S};
|
||||
|
||||
handle_event(Event, StateName, S) ->
|
||||
{next_state, StateName, S}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_sync_event/4
|
||||
%% Called when gen_fsm:sync_send_all_state_event/2,3 is invoked
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {reply, Reply, NextStateName, NextStateData} |
|
||||
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData} |
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event({debug_level, N}, From, StateName, S) ->
|
||||
{reply, ok, StateName, S#eldap{debug_level = N}};
|
||||
|
||||
handle_sync_event(Event, From, StateName, S) ->
|
||||
{reply, {StateName, S}, StateName, S};
|
||||
|
||||
handle_sync_event(Event, From, StateName, S) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, StateName, S}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: handle_info/3
|
||||
%% Returns: {next_state, NextStateName, NextStateData} |
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {stop, Reason, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%%
|
||||
%% Packets arriving in various states
|
||||
%%
|
||||
handle_info({tcp, Socket, Data}, connecting, S) ->
|
||||
log1("eldap. tcp packet received when disconnected!~n~p~n", [Data], S),
|
||||
{next_state, connecting, S};
|
||||
|
||||
handle_info({tcp, Socket, Data}, wait_bind_response, S) ->
|
||||
cancel_timer(S#eldap.bind_timer),
|
||||
case catch recvd_wait_bind_response(Data, S) of
|
||||
bound -> {next_state, active, S};
|
||||
{fail_bind, Reason} -> close_and_retry(S),
|
||||
{next_state, connecting, S#eldap{fd = null}};
|
||||
{'EXIT', Reason} -> close_and_retry(S),
|
||||
{next_state, connecting, S#eldap{fd = null}};
|
||||
{error, Reason} -> close_and_retry(S),
|
||||
{next_state, connecting, S#eldap{fd = null}}
|
||||
end;
|
||||
|
||||
handle_info({tcp, Socket, Data}, active, S) ->
|
||||
case catch recvd_packet(Data, S) of
|
||||
{reply, Reply, To, NewS} -> gen_fsm:reply(To, Reply),
|
||||
{next_state, active, NewS};
|
||||
{ok, NewS} -> {next_state, active, NewS};
|
||||
{'EXIT', Reason} -> {next_state, active, S};
|
||||
{error, Reason} -> {next_state, active, S}
|
||||
end;
|
||||
|
||||
handle_info({tcp_closed, Socket}, All_fsm_states, S) ->
|
||||
F = fun(Id, [{Timer, From, Name}|Res]) ->
|
||||
gen_fsm:reply(From, {error, tcp_closed}),
|
||||
cancel_timer(Timer)
|
||||
end,
|
||||
dict:map(F, S#eldap.dict),
|
||||
retry_connect(),
|
||||
{next_state, connecting, S#eldap{fd = null,
|
||||
dict = dict:new()}};
|
||||
|
||||
handle_info({tcp_error, Socket, Reason}, Fsm_state, S) ->
|
||||
log1("eldap received tcp_error: ~p~nIn State: ~p~n", [Reason, Fsm_state], S),
|
||||
{next_state, Fsm_state, S};
|
||||
%%
|
||||
%% Timers
|
||||
%%
|
||||
handle_info({timeout, Timer, {cmd_timeout, Id}}, active, S) ->
|
||||
case cmd_timeout(Timer, Id, S) of
|
||||
{reply, To, Reason, NewS} -> gen_fsm:reply(To, Reason),
|
||||
{next_state, active, NewS};
|
||||
{error, Reason} -> {next_state, active, S}
|
||||
end;
|
||||
|
||||
handle_info({timeout, retry_connect}, connecting, S) ->
|
||||
{ok, NextState, NewS} = connect_bind(S),
|
||||
{next_state, NextState, NewS};
|
||||
|
||||
handle_info({timeout, Timer, bind_timeout}, wait_bind_response, S) ->
|
||||
close_and_retry(S),
|
||||
{next_state, connecting, S#eldap{fd = null}};
|
||||
|
||||
%%
|
||||
%% Make sure we don't fill the message queue with rubbish
|
||||
%%
|
||||
handle_info(Info, StateName, S) ->
|
||||
log1("eldap. Unexpected Info: ~p~nIn state: ~p~n when StateData is: ~p~n",
|
||||
[Info, StateName, S], S),
|
||||
{next_state, StateName, S}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
%% Returns: any
|
||||
%%----------------------------------------------------------------------
|
||||
terminate(Reason, StateName, StatData) ->
|
||||
ok.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: code_change/4
|
||||