diff options
Diffstat (limited to 'lib/eldap')
-rw-r--r-- | lib/eldap/asn1/ELDAPv3.asn1 | 12 | ||||
-rw-r--r-- | lib/eldap/doc/src/eldap.xml | 76 | ||||
-rw-r--r-- | lib/eldap/doc/src/notes.xml | 60 | ||||
-rw-r--r-- | lib/eldap/src/eldap.erl | 168 | ||||
-rw-r--r-- | lib/eldap/test/Makefile | 1 | ||||
-rw-r--r-- | lib/eldap/test/README | 2 | ||||
-rw-r--r-- | lib/eldap/test/eldap_basic_SUITE.erl | 28 | ||||
-rw-r--r-- | lib/eldap/test/eldap_connections_SUITE.erl | 147 | ||||
-rw-r--r-- | lib/eldap/vsn.mk | 3 |
9 files changed, 476 insertions, 21 deletions
diff --git a/lib/eldap/asn1/ELDAPv3.asn1 b/lib/eldap/asn1/ELDAPv3.asn1 index 72b87d7221..3fe7e815cc 100644 --- a/lib/eldap/asn1/ELDAPv3.asn1 +++ b/lib/eldap/asn1/ELDAPv3.asn1 @@ -274,5 +274,17 @@ IntermediateResponse ::= [APPLICATION 25] SEQUENCE { responseName [0] LDAPOID OPTIONAL, responseValue [1] OCTET STRING OPTIONAL } +-- Extended syntax for Password Modify (RFC 3062, Section 2) + +-- passwdModifyOID OBJECT IDENTIFIER ::= 1.3.6.1.4.1.4203.1.11.1 + +PasswdModifyRequestValue ::= SEQUENCE { + userIdentity [0] OCTET STRING OPTIONAL, + oldPasswd [1] OCTET STRING OPTIONAL, + newPasswd [2] OCTET STRING OPTIONAL } + +PasswdModifyResponseValue ::= SEQUENCE { + genPasswd [0] OCTET STRING OPTIONAL } + END diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml index 8009a8d6a3..b68115cd82 100644 --- a/lib/eldap/doc/src/eldap.xml +++ b/lib/eldap/doc/src/eldap.xml @@ -48,7 +48,7 @@ scope() See baseObject/0, singleLevel/0, wholeSubtree/0 dereference() See neverDerefAliases/0, derefInSearching/0, derefFindingBaseObj/0, derefAlways/0 filter() See present/1, substrings/2, equalityMatch/2, greaterOrEqual/2, lessOrEqual/2, - approxMatch/2, + approxMatch/2, extensibleMatch/2, 'and'/1, 'or'/1, 'not'/1. </pre> <p></p> @@ -75,7 +75,9 @@ filter() See present/1, substrings/2, <p>Setup a connection to an LDAP server, the <c>HOST</c>'s are tried in order.</p> <p>The log function takes three arguments, <c>fun(Level, FormatString, [FormatArg]) end</c>.</p> <p>Timeout set the maximum time in milliseconds that each server request may take.</p> - <p>Currently, the only TCP socket option accepted is <c>inet6</c>. Default is <c>inet</c>.</p> + <p>All TCP socket options are accepted except + <c>active</c>, <c>binary</c>, <c>deliver</c>, <c>list</c>, <c>mode</c> and <c>packet</c> + </p> </desc> </func> <func> @@ -105,19 +107,23 @@ filter() See present/1, substrings/2, </type> <desc> <p>Upgrade the connection associated with <c>Handle</c> to a tls connection if possible.</p> - <p>The upgrade is done in two phases: first the server is asked for permission to upgrade. Second, if the request is acknowledged, the upgrade is performed.</p> - <p>Error responese from phase one will not affect the current encryption state of the connection. Those responses are:</p> + <p>The upgrade is done in two phases: first the server is asked for permission to upgrade. Second, if the request is acknowledged, the upgrade to tls is performed.</p> + <p>Error responses from phase one will not affect the current encryption state of the connection. Those responses are:</p> <taglist> <tag><c>tls_already_started</c></tag> <item>The connection is already encrypted. The connection is not affected.</item> <tag><c>{response,ResponseFromServer}</c></tag> <item>The upgrade was refused by the LDAP server. The <c>ResponseFromServer</c> is an atom delivered byt the LDAP server explained in section 2.3 of rfc 2830. The connection is not affected, so it is still un-encrypted.</item> </taglist> - <p>Errors in the seconde phase will however end the connection:</p> + <p>Errors in the second phase will however end the connection:</p> <taglist> <tag><c>Error</c></tag> <item>Any error responded from ssl:connect/3</item> </taglist> + <p>The <c>Timeout</c> parameter is for the actual tls upgrade (phase 2) while the timeout in + <seealso marker="#open/2">erl_tar:open/2</seealso> is used for the initial negotiation about + upgrade (phase 1). + </p> </desc> </func> <func> @@ -212,6 +218,46 @@ filter() See present/1, substrings/2, </desc> </func> <func> + <name>modify_password(Handle, Dn, NewPasswd) -> ok | {ok, GenPasswd} | {error, Reason}</name> + <fsummary>Modify the password of a user.</fsummary> + <type> + <v>Dn = string()</v> + <v>NewPasswd = string()</v> + </type> + <desc> + <p>Modify the password of a user. See <seealso marker="#modify_password/4">modify_password/4</seealso>.</p> + </desc> + </func> + <func> + <name>modify_password(Handle, Dn, NewPasswd, OldPasswd) -> ok | {ok, GenPasswd} | {error, Reason}</name> + <fsummary>Modify the password of a user.</fsummary> + <type> + <v>Dn = string()</v> + <v>NewPasswd = string()</v> + <v>OldPasswd = string()</v> + <v>GenPasswd = string()</v> + </type> + <desc> + <p>Modify the password of a user.</p> + <list type="bulleted"> + <item> + <p><c>Dn</c>. The user to modify. Should be "" if the + modify request is for the user of the LDAP session.</p> + </item> + <item> + <p><c>NewPasswd</c>. The new password to set. Should be "" + if the server is to generate the password. In this case, + the result will be <c>{ok, GenPasswd}</c>.</p> + </item> + <item> + <p><c>OldPasswd</c>. Sometimes required by server policy + for a user to change their password. If not required, use + <seealso marker="#modify_password/3">modify_password/3</seealso>.</p> + </item> + </list> + </desc> + </func> + <func> <name>modify_dn(Handle, Dn, NewRDN, DeleteOldRDN, NewSupDN) -> ok | {error, Reason}</name> <fsummary>Modify the DN of an entry.</fsummary> <type> @@ -222,9 +268,9 @@ filter() See present/1, substrings/2, </type> <desc> <p> Modify the DN of an entry. <c>DeleteOldRDN</c> indicates - whether the current RDN should be removed after operation. - <c>NewSupDN</c> should be "" if the RDN should not be moved or the new parent which - the RDN will be moved to.</p> + whether the current RDN should be removed from the attribute list after the after operation. + <c>NewSupDN</c> is the new parent that the RDN shall be moved to. If the old parent should + remain as parent, <c>NewSupDN</c> shall be "".</p> <pre> modify_dn(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com ", "cn=Bill Jr Valentine", true, "") @@ -251,6 +297,10 @@ filter() See present/1, substrings/2, Filter = eldap:substrings("cn", [{any,"V"}]), search(Handle, [{base, "dc=example, dc=com"}, {filter, Filter}, {attributes, ["cn"]}]), </pre> + <p>The <c>timeout</c> option in the <c>SearchOptions</c> is for the ldap server, while + the timeout in <seealso marker="#open/2">erl_tar:open/2</seealso> is used for each + individual request in the search operation. + </p> </desc> </func> @@ -346,6 +396,16 @@ filter() See present/1, substrings/2, <desc> <p>Create a approximation match filter.</p> </desc> </func> <func> + <name>extensibleMatch(MatchValue, OptionalAttrs) -> filter()</name> + <fsummary>Create search filter option.</fsummary> + <type> + <v>MatchValue = string()</v> + <v>OptionalAttrs = [Attr]</v> + <v>Attr = {matchingRule,string()} | {type,string()} | {dnAttributes,boolean()}</v> + </type> + <desc> <p>Creates an extensible match filter. For example, <c>eldap:extensibleMatch("Bar",[{type,"sn"},{matchingRule,"caseExactMatch"}]))</c> creates a filter which performs a <c>caseExactMatch</c> on the attribute <c>sn</c> and matches with the value <c>"Bar"</c>. The default value of <c>dnAttributes</c> is <c>false</c>.</p> </desc> + </func> + <func> <name>'and'([Filter]) -> filter()</name> <fsummary>Create search filter option.</fsummary> <type> diff --git a/lib/eldap/doc/src/notes.xml b/lib/eldap/doc/src/notes.xml index 089bb731d4..e5cbcb26ff 100644 --- a/lib/eldap/doc/src/notes.xml +++ b/lib/eldap/doc/src/notes.xml @@ -30,7 +30,65 @@ </header> <p>This document describes the changes made to the Eldap application.</p> - <section><title>Eldap 1.0.3</title> +<section><title>Eldap 1.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed that eldap:open did not use the Timeout parameter + when calling ssl:connect. (Thanks Wiesław Bieniek for + reporting)</p> + <p> + Own Id: OTP-12311</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Added the LDAP filter <c>extensibleMatch</c>.</p> + <p> + Own Id: OTP-12174</p> + </item> + </list> + </section> + +</section> + + <section><title>Eldap 1.0.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + <c>eldap:open/2</c> and <c>eldap:open/3</c> gave wrong + return values for option errors.</p> + <p> + Own Id: OTP-12182</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Nearly all TCP options are possible to give in the + <c>eldap:open/2</c> call.</p> + <p> + Own Id: OTP-12171</p> + </item> + </list> + </section> + +</section> + +<section><title>Eldap 1.0.3</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/eldap/src/eldap.erl b/lib/eldap/src/eldap.erl index 1cd328cde3..c636e0e0cd 100644 --- a/lib/eldap/src/eldap.erl +++ b/lib/eldap/src/eldap.erl @@ -12,8 +12,11 @@ -vc('$Id$ '). -export([open/1,open/2,simple_bind/3,controlling_process/2, start_tls/2, start_tls/3, + modify_password/3, modify_password/4, + getopts/2, baseObject/0,singleLevel/0,wholeSubtree/0,close/1, equalityMatch/2,greaterOrEqual/2,lessOrEqual/2, + extensibleMatch/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,parse_dn/1, @@ -92,6 +95,32 @@ start_tls(Handle, TlsOptions, Timeout) -> recv(Handle). %%% -------------------------------------------------------------------- +%%% Modify the password of a user. +%%% +%%% Dn - Name of the entry to modify. If empty, the session user. +%%% NewPasswd - New password. If empty, the server returns a new password. +%%% OldPasswd - Original password for server verification, may be empty. +%%% +%%% Returns: ok | {ok, GenPasswd} | {error, term()} +%%% -------------------------------------------------------------------- +modify_password(Handle, Dn, NewPasswd) -> + modify_password(Handle, Dn, NewPasswd, []). + +modify_password(Handle, Dn, NewPasswd, OldPasswd) + when is_pid(Handle), is_list(Dn), is_list(NewPasswd), is_list(OldPasswd) -> + send(Handle, {passwd_modify,optional(Dn),optional(NewPasswd),optional(OldPasswd)}), + recv(Handle). + +%%% -------------------------------------------------------------------- +%%% Ask for option values on the socket. +%%% Warning: This is an undocumented function for testing purposes only. +%%% Use at own risk... +%%% -------------------------------------------------------------------- +getopts(Handle, OptNames) when is_pid(Handle), is_list(OptNames) -> + send(Handle, {getopts, OptNames}), + recv(Handle). + +%%% -------------------------------------------------------------------- %%% Shutdown connection (and process) asynchronous. %%% -------------------------------------------------------------------- @@ -340,6 +369,27 @@ substrings(Type, SubStr) when is_list(Type), is_list(SubStr) -> {substrings,#'SubstringFilter'{type = Type, substrings = Ss}}. +%%% +%%% Filter for extensibleMatch +%%% +extensibleMatch(MatchValue, OptArgs) -> + MatchingRuleAssertion = + mra(OptArgs, #'MatchingRuleAssertion'{matchValue = MatchValue}), + {extensibleMatch, MatchingRuleAssertion}. + +mra([{matchingRule,Val}|T], Ack) when is_list(Val) -> + mra(T, Ack#'MatchingRuleAssertion'{matchingRule=Val}); +mra([{type,Val}|T], Ack) when is_list(Val) -> + mra(T, Ack#'MatchingRuleAssertion'{type=Val}); +mra([{dnAttributes,true}|T], Ack) -> + mra(T, Ack#'MatchingRuleAssertion'{dnAttributes="TRUE"}); +mra([{dnAttributes,false}|T], Ack) -> + mra(T, Ack#'MatchingRuleAssertion'{dnAttributes="FALSE"}); +mra([H|_], _) -> + throw({error,{extensibleMatch_arg,H}}); +mra([], Ack) -> + Ack. + %%% -------------------------------------------------------------------- %%% Worker process. We keep track of a controlling process to %%% be able to terminate together with it. @@ -374,24 +424,35 @@ parse_args([{sslopts, Opts}|T], Cpid, Data) when is_list(Opts) -> parse_args([{sslopts, _}|T], Cpid, Data) -> parse_args(T, Cpid, Data); parse_args([{tcpopts, Opts}|T], Cpid, Data) when is_list(Opts) -> - parse_args(T, Cpid, Data#eldap{tcp_opts = inet6_opt(Opts) ++ Data#eldap.tcp_opts}); + parse_args(T, Cpid, Data#eldap{tcp_opts = tcp_opts(Opts,Cpid,Data#eldap.tcp_opts)}); parse_args([{log, F}|T], Cpid, Data) when is_function(F) -> parse_args(T, Cpid, Data#eldap{log = F}); parse_args([{log, _}|T], Cpid, Data) -> parse_args(T, Cpid, Data); parse_args([H|_], Cpid, _) -> send(Cpid, {error,{wrong_option,H}}), + unlink(Cpid), exit(wrong_option); parse_args([], _, Data) -> Data. -inet6_opt(Opts) -> - case proplists:get_value(inet6, Opts) of +tcp_opts([Opt|Opts], Cpid, Acc) -> + Key = if is_atom(Opt) -> Opt; + is_tuple(Opt) -> element(1,Opt) + end, + case lists:member(Key,[active,binary,deliver,list,mode,packet]) of + false -> + tcp_opts(Opts, Cpid, [Opt|Acc]); true -> - [inet6]; - _ -> - [] - end. + tcp_opts_error(Opt, Cpid) + end; +tcp_opts([], _Cpid, Acc) -> Acc. + +tcp_opts_error(Opt, Cpid) -> + send(Cpid, {error, {{forbidden_tcp_option,Opt}, + "This option affects the eldap functionality and can't be set by user"}}), + unlink(Cpid), + exit(forbidden_tcp_option). %%% Try to connect to the hosts in the listed order, %%% and stop with the first one to which a successful @@ -416,7 +477,8 @@ do_connect(Host, Data, Opts) when Data#eldap.ldaps == false -> Data#eldap.timeout); do_connect(Host, Data, Opts) when Data#eldap.ldaps == true -> ssl:connect(Host, Data#eldap.port, - Opts ++ Data#eldap.tls_opts ++ Data#eldap.tcp_opts). + Opts ++ Data#eldap.tls_opts ++ Data#eldap.tcp_opts, + Data#eldap.timeout). loop(Cpid, Data) -> receive @@ -462,10 +524,45 @@ loop(Cpid, Data) -> send(From,Res), ?MODULE:loop(Cpid, NewData); + {From, {passwd_modify,Dn,NewPasswd,OldPasswd}} -> + {Res,NewData} = do_passwd_modify(Data, Dn, NewPasswd, OldPasswd), + send(From, Res), + ?MODULE:loop(Cpid, NewData); + {_From, close} -> unlink(Cpid), exit(closed); + {From, {getopts, OptNames}} -> + Result = + try + [case OptName of + port -> {port, Data#eldap.port}; + log -> {log, Data#eldap.log}; + timeout -> {timeout, Data#eldap.timeout}; + ssl -> {ssl, Data#eldap.ldaps}; + {sslopts, SslOptNames} when Data#eldap.using_tls==true -> + case ssl:getopts(Data#eldap.fd, SslOptNames) of + {ok,SslOptVals} -> {sslopts, SslOptVals}; + {error,Reason} -> throw({error,Reason}) + end; + {sslopts, _} -> + throw({error,no_tls}); + {tcpopts, TcpOptNames} -> + case inet:getopts(Data#eldap.fd, TcpOptNames) of + {ok,TcpOptVals} -> {tcpopts, TcpOptVals}; + {error,Posix} -> throw({error,Posix}) + end + end || OptName <- OptNames] + of + OptsList -> {ok,OptsList} + catch + throw:Error -> Error; + Class:Error -> {error,{Class,Error}} + end, + send(From, Result), + ?MODULE:loop(Cpid, Data); + {Cpid, 'EXIT', Reason} -> ?PRINT("Got EXIT from Cpid, reason=~p~n",[Reason]), exit(Reason); @@ -722,6 +819,60 @@ do_modify_0(Data, Obj, Mod) -> check_reply(Data#eldap{id = Id}, Resp, modifyResponse). %%% -------------------------------------------------------------------- +%%% PasswdModifyRequest +%%% -------------------------------------------------------------------- + +-define(PASSWD_MODIFY_OID, "1.3.6.1.4.1.4203.1.11.1"). + +do_passwd_modify(Data, Dn, NewPasswd, OldPasswd) -> + case catch do_passwd_modify_0(Data, Dn, NewPasswd, OldPasswd) of + {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data}; + {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data}; + {ok,NewData} -> {ok,NewData}; + {ok,Passwd,NewData} -> {{ok, Passwd},NewData}; + Else -> {ldap_closed_p(Data, Else),Data} + end. + +do_passwd_modify_0(Data, Dn, NewPasswd, OldPasswd) -> + Req = #'PasswdModifyRequestValue'{userIdentity = Dn, + oldPasswd = OldPasswd, + newPasswd = NewPasswd}, + log2(Data, "modify password request = ~p~n", [Req]), + {ok, Bytes} = 'ELDAPv3':encode('PasswdModifyRequestValue', Req), + ExtReq = #'ExtendedRequest'{requestName = ?PASSWD_MODIFY_OID, + requestValue = Bytes}, + Id = bump_id(Data), + log2(Data, "extended request = ~p~n", [ExtReq]), + Reply = request(Data#eldap.fd, Data, Id, {extendedReq, ExtReq}), + log2(Data, "modify password reply = ~p~n", [Reply]), + exec_passwd_modify_reply(Data#eldap{id = Id}, Reply). + +exec_passwd_modify_reply(Data, {ok,Msg}) when + Msg#'LDAPMessage'.messageID == Data#eldap.id -> + case Msg#'LDAPMessage'.protocolOp of + {extendedResp, Result} -> + case Result#'ExtendedResponse'.resultCode of + success -> + case Result#'ExtendedResponse'.responseValue of + asn1_NOVALUE -> + {ok, Data}; + Value -> + case 'ELDAPv3':decode('PasswdModifyResponseValue', Value) of + {ok,#'PasswdModifyResponseValue'{genPasswd = Passwd}} -> + {ok, Passwd, Data}; + Error -> + throw(Error) + end + end; + Error -> + {error, {response,Error}} + end; + Other -> {error, Other} + end; +exec_passwd_modify_reply(_, Error) -> + {error, Error}. + +%%% -------------------------------------------------------------------- %%% modifyDNRequest %%% -------------------------------------------------------------------- @@ -811,6 +962,7 @@ v_filter({lessOrEqual,AV}) -> {lessOrEqual,AV}; v_filter({approxMatch,AV}) -> {approxMatch,AV}; v_filter({present,A}) -> {present,A}; v_filter({substrings,S}) when is_record(S,'SubstringFilter') -> {substrings,S}; +v_filter({extensibleMatch,S}) when is_record(S,'MatchingRuleAssertion') -> {extensibleMatch,S}; v_filter(_Filter) -> throw({error,concat(["unknown filter: ",_Filter])}). v_modifications(Mods) -> diff --git a/lib/eldap/test/Makefile b/lib/eldap/test/Makefile index 3c5810eece..24e71cebaa 100644 --- a/lib/eldap/test/Makefile +++ b/lib/eldap/test/Makefile @@ -28,6 +28,7 @@ INCLUDES= -I. -I ../include # ---------------------------------------------------- MODULES= \ + eldap_connections_SUITE \ eldap_basic_SUITE ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/eldap/test/README b/lib/eldap/test/README index 8774db1504..ec774c1ae3 100644 --- a/lib/eldap/test/README +++ b/lib/eldap/test/README @@ -19,7 +19,7 @@ This will however not work, since slapd is guarded by apparmor that checks that To make a local extension of alowed operations: sudo emacs /etc/apparmor.d/local/usr.sbin.slapd -and, after the change (yes, at least on Ubuntu it is right to edit ../local/.. but run with an other file) : +and, after the change (yes, at least on Ubuntu it is right to edit ../local/.. but run with another file): sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.slapd diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl index 6c3d303da0..7f2be54b71 100644 --- a/lib/eldap/test/eldap_basic_SUITE.erl +++ b/lib/eldap/test/eldap_basic_SUITE.erl @@ -106,7 +106,9 @@ api(doc) -> "Basic test that all api functions works as expected"; api(suite) -> []; api(Config) -> {Host,Port} = proplists:get_value(ldap_server, Config), - {ok, H} = eldap:open([Host], [{port,Port}]), + {ok, H} = eldap:open([Host], [{port,Port} + ,{log,fun(Lvl,Fmt,Args)-> io:format("~p: ~s",[Lvl,io_lib:format(Fmt,Args)]) end} + ]), %% {ok, H} = eldap:open([Host], [{port,Port+1}, {ssl, true}]), do_api_checks(H, Config), eldap:close(H), @@ -198,6 +200,7 @@ do_api_checks(H, Config) -> chk_add(H, BasePath), {ok,FB} = chk_search(H, BasePath), chk_modify(H, FB), + chk_modify_password(H, FB), chk_delete(H, BasePath), chk_modify_dn(H, FB). @@ -232,6 +235,12 @@ chk_search(H, BasePath) -> {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(F_AND), F_NOT = eldap:'and'([eldap:present("objectclass"), eldap:'not'(eldap:present("ou"))]), {ok, #eldap_search_result{entries=[#eldap_entry{}, #eldap_entry{}]}} = Search(F_NOT), + {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(eldap:extensibleMatch("Bar",[{type,"sn"},{matchingRule,"caseExactMatch"}])), + {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(eldap:extensibleMatch("Bar",[{type,"sn"},{matchingRule,"2.5.13.5"}])), + {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(eldap:extensibleMatch("Bar",[{type,"sn"},{matchingRule,"caseIgnoreMatch"}])), + {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(eldap:extensibleMatch("bar",[{type,"sn"},{matchingRule,"caseIgnoreMatch"}])), + {ok, #eldap_search_result{entries=[]}} = Search(eldap:extensibleMatch("bar",[{type,"sn"},{matchingRule,"gluffgluff"}])), + {ok, #eldap_search_result{entries=[]}} = Search(eldap:extensibleMatch("bar",[{type,"sn"},{matchingRule,"caseExactMatch"}])), {ok,FB}. %% FIXME chk_modify(H, FB) -> @@ -242,6 +251,23 @@ chk_modify(H, FB) -> %% DELETE ATTR ok = eldap:modify(H, FB, [eldap:mod_delete("telephoneNumber", [])]). +chk_modify_password(H, FB) -> + %% Change password, and ensure we can bind with it. + ok = eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"), + ok = eldap:modify_password(H, FB, "example"), + ok = eldap:simple_bind(H, FB, "example"), + %% Change password to a server generated value. + ok = eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"), + {ok, Passwd} = eldap:modify_password(H, FB, []), + ok = eldap:simple_bind(H, FB, Passwd), + %% Change own password to server generated value. + {ok, NewPasswd} = eldap:modify_password(H, [], [], Passwd), + ok = eldap:simple_bind(H, FB, NewPasswd), + %% Change own password to explicit value. + ok = eldap:modify_password(H, [], "example", NewPasswd), + ok = eldap:simple_bind(H, FB, "example"), + %% Restore original binding. + ok = eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"). chk_delete(H, BasePath) -> {error, entryAlreadyExists} = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath, diff --git a/lib/eldap/test/eldap_connections_SUITE.erl b/lib/eldap/test/eldap_connections_SUITE.erl new file mode 100644 index 0000000000..c5460fef09 --- /dev/null +++ b/lib/eldap/test/eldap_connections_SUITE.erl @@ -0,0 +1,147 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012-2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(eldap_connections_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +%-include_lib("eldap/include/eldap.hrl"). + + +all() -> + [ + {group, v4}, + {group, v6} + ]. + + +init_per_group(v4, Config) -> + [{listen_opts, []}, + {listen_host, "localhost"}, + {connect_opts, []} + | Config]; +init_per_group(v6, Config) -> + {ok, Hostname} = inet:gethostname(), + case lists:member(list_to_atom(Hostname), ct:get_config(ipv6_hosts,[])) of + true -> + [{listen_opts, [inet6]}, + {listen_host, "::"}, + {connect_opts, [{tcpopts,[inet6]}]} + | Config]; + false -> + {skip, io_lib:format("~p is not an ipv6_host",[Hostname])} + end. + + +end_per_group(_GroupName, Config) -> + Config. + + +groups() -> + [{v4, [], [tcp_connection, tcp_connection_option]}, + {v6, [], [tcp_connection, tcp_connection_option]} + ]. + + +init_per_suite(Config) -> Config. + + +end_per_suite(_Config) -> ok. + + +init_per_testcase(_TestCase, Config) -> + case gen_tcp:listen(0, proplists:get_value(listen_opts,Config)) of + {ok,LSock} -> + {ok,{_,Port}} = inet:sockname(LSock), + [{listen_socket,LSock}, + {listen_port,Port} + | Config]; + Other -> + {fail, Other} + end. + + +end_per_testcase(_TestCase, Config) -> + catch gen_tcp:close( proplists:get_value(listen_socket, Config) ). + +%%%================================================================ +%%% +%%% Test cases +%%% +%%%---------------------------------------------------------------- +tcp_connection(Config) -> + Host = proplists:get_value(listen_host, Config), + Port = proplists:get_value(listen_port, Config), + Opts = proplists:get_value(connect_opts, Config), + case eldap:open([Host], [{port,Port}|Opts]) of + {ok,_H} -> + Sl = proplists:get_value(listen_socket, Config), + case gen_tcp:accept(Sl,1000) of + {ok,_S} -> ok; + {error,timeout} -> ct:fail("server side accept timeout",[]) + end; + Other -> ct:fail("eldap:open failed: ~p",[Other]) + end. + + +%%%---------------------------------------------------------------- +tcp_connection_option(Config) -> + Host = proplists:get_value(listen_host, Config), + Port = proplists:get_value(listen_port, Config), + Opts = proplists:get_value(connect_opts, Config), + Sl = proplists:get_value(listen_socket, Config), + + %% Make an option value to test. The option must be implemented on all + %% platforms that we test on. Must check what the default value is + %% so we don't happen to choose that particular value. + {ok,[{linger,DefaultLinger}]} = inet:getopts(Sl, [linger]), + TestLinger = case DefaultLinger of + {false,_} -> {true,5}; + {true,_} -> {false,0} + end, + + case catch eldap:open([Host], + [{port,Port},{tcpopts,[{linger,TestLinger}]}|Opts]) of + {ok,H} -> + case gen_tcp:accept(Sl,1000) of + {ok,_} -> + case eldap:getopts(H, [{tcpopts,[linger]}]) of + {ok,[{tcpopts,[{linger,ActualLinger}]}]} -> + case ActualLinger of + TestLinger -> + ok; + DefaultLinger -> + ct:fail("eldap:getopts: 'linger' didn't change," + " got ~p (=default) expected ~p", + [ActualLinger,TestLinger]); + _ -> + ct:fail("eldap:getopts: bad 'linger', got ~p expected ~p", + [ActualLinger,TestLinger]) + end; + Other -> + ct:fail("eldap:getopts: bad result ~p",[Other]) + end; + {error,timeout} -> + ct:fail("server side accept timeout",[]) + end; + + Other -> + ct:fail("eldap:open failed: ~p",[Other]) + end. diff --git a/lib/eldap/vsn.mk b/lib/eldap/vsn.mk index efdc30b476..432ba2e742 100644 --- a/lib/eldap/vsn.mk +++ b/lib/eldap/vsn.mk @@ -1,2 +1 @@ -ELDAP_VSN = 1.0.3 - +ELDAP_VSN = 1.1 |