%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2012-2013. 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_basic_SUITE). -compile(export_all). %%-include_lib("common_test/include/ct.hrl"). -include_lib("test_server/include/test_server.hrl"). -include_lib("eldap/include/eldap.hrl"). -define(TIMEOUT, 120000). % 2 min init_per_suite(Config) -> StartSsl = try ssl:start() catch Error:Reason -> {skip, lists:flatten(io_lib:format("eldap init_per_suite failed to start ssl Error=~p Reason=~p", [Error, Reason]))} end, case StartSsl of ok -> chk_config(ldap_server, {"localhost",9876}, chk_config(ldaps_server, {"localhost",9877}, Config)); _ -> StartSsl end. end_per_suite(_Config) -> ok. init_per_testcase(_TestCase, Config0) -> {EldapHost,Port} = proplists:get_value(ldap_server,Config0), try {ok, Handle} = eldap:open([EldapHost], [{port,Port}]), ok = eldap:simple_bind(Handle, "cn=Manager,dc=ericsson,dc=se", "hejsan"), {ok, MyHost} = inet:gethostname(), Path = "dc="++MyHost++",dc=ericsson,dc=se", eldap:add(Handle,"dc=ericsson,dc=se", [{"objectclass", ["dcObject", "organization"]}, {"dc", ["ericsson"]}, {"o", ["Testing"]}]), eldap:add(Handle,Path, [{"objectclass", ["dcObject", "organization"]}, {"dc", [MyHost]}, {"o", ["Test machine"]}]), [{eldap_path,Path}|Config0] catch error:{badmatch,Error} -> io:format("Eldap init error ~p~n ~p~n",[Error, erlang:get_stacktrace()]), {skip, lists:flatten(io_lib:format("Ldap init failed with host ~p:~p. Error=~p", [EldapHost,Port,Error]))} end. end_per_testcase(_TestCase, Config) -> {EHost, Port} = proplists:get_value(ldap_server, Config), Path = proplists:get_value(eldap_path, Config), {ok, H} = eldap:open([EHost], [{port, Port}]), ok = eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"), case eldap:search(H, [{base, Path}, {filter, eldap:present("objectclass")}, {scope, eldap:wholeSubtree()}]) of {ok, {eldap_search_result, Entries, _}} -> [ok = eldap:delete(H, Entry) || {eldap_entry, Entry, _} <- Entries]; _ -> ignore end, ok. %% suite() -> all() -> [app, appup, api, ssl_api, start_tls, tls_operations, start_tls_twice, start_tls_on_ssl ]. app(doc) -> "Test that the eldap app file is ok"; app(suite) -> []; app(Config) when is_list(Config) -> ok = test_server:app_test(eldap). %% Test that the eldap appup file is ok appup(Config) when is_list(Config) -> ok = test_server:appup_test(eldap). 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} ,{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), ok. ssl_api(doc) -> "Basic test that all api functions works as expected"; ssl_api(suite) -> []; ssl_api(Config) -> {Host,Port} = proplists:get_value(ldaps_server, Config), {ok, H} = eldap:open([Host], [{port,Port}, {ssl,true}]), do_api_checks(H, Config), eldap:close(H), ok. start_tls(doc) -> "Test that an existing (tcp) connection can be upgraded to tls"; start_tls(suite) -> []; start_tls(Config) -> {Host,Port} = proplists:get_value(ldap_server, Config), {ok, H} = eldap:open([Host], [{port,Port}]), ok = eldap:start_tls(H, [ {keyfile, filename:join([proplists:get_value(data_dir,Config), "certs/client/key.pem"])} ]), eldap:close(H). tls_operations(doc) -> "Test that an upgraded connection is usable for ldap stuff"; tls_operations(suite) -> []; tls_operations(Config) -> {Host,Port} = proplists:get_value(ldap_server, Config), {ok, H} = eldap:open([Host], [{port,Port}]), ok = eldap:start_tls(H, [ {keyfile, filename:join([proplists:get_value(data_dir,Config), "certs/client/key.pem"])} ]), do_api_checks(H, Config), eldap:close(H). start_tls_twice(doc) -> "Test that start_tls on an already upgraded connection fails"; start_tls_twice(suite) -> []; start_tls_twice(Config) -> {Host,Port} = proplists:get_value(ldap_server, Config), {ok, H} = eldap:open([Host], [{port,Port}]), ok = eldap:start_tls(H, []), {error,tls_already_started} = eldap:start_tls(H, []), do_api_checks(H, Config), eldap:close(H). start_tls_on_ssl(doc) -> "Test that start_tls on an ldaps connection fails"; start_tls_on_ssl(suite) -> []; start_tls_on_ssl(Config) -> {Host,Port} = proplists:get_value(ldaps_server, Config), {ok, H} = eldap:open([Host], [{port,Port}, {ssl,true}]), {error,tls_already_started} = eldap:start_tls(H, []), do_api_checks(H, Config), eldap:close(H). %%%-------------------------------------------------------------------------------- chk_config(Key, Default, Config) -> case catch ct:get_config(ldap_server, undefined) of undefined -> [{Key,Default} | Config ]; {'EXIT',_} -> [{Key,Default} | Config ]; Value -> [{Key,Value} | Config] end. do_api_checks(H, Config) -> BasePath = proplists:get_value(eldap_path, Config), All = fun(Where) -> eldap:search(H, #eldap_search{base=Where, filter=eldap:present("objectclass"), scope= eldap:wholeSubtree()}) end, {ok, #eldap_search_result{entries=[_XYZ]}} = All(BasePath), %% ct:log("XYZ=~p",[_XYZ]), {error, noSuchObject} = All("cn=Bar,"++BasePath), {error, _} = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath, [{"objectclass", ["person"]}, {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]), eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"), 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). chk_add(H, BasePath) -> ok = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath, [{"objectclass", ["person"]}, {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]), {error, entryAlreadyExists} = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath, [{"objectclass", ["person"]}, {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]), ok = eldap:add(H, "cn=Foo Bar," ++ BasePath, [{"objectclass", ["person"]}, {"cn", ["Foo Bar"]}, {"sn", ["Bar"]}, {"telephoneNumber", ["555-1232", "555-5432"]}]), ok = eldap:add(H, "ou=Team," ++ BasePath, [{"objectclass", ["organizationalUnit"]}, {"ou", ["Team"]}]). chk_search(H, BasePath) -> Search = fun(Filter) -> eldap:search(H, #eldap_search{base=BasePath, filter=Filter, scope=eldap:singleLevel()}) end, JJSR = {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(eldap:equalityMatch("sn", "Jonsson")), JJSR = Search(eldap:substrings("sn", [{any, "ss"}])), FBSR = {ok, #eldap_search_result{entries=[#eldap_entry{object_name=FB}]}} = Search(eldap:substrings("sn", [{any, "a"}])), FBSR = Search(eldap:substrings("sn", [{initial, "B"}])), FBSR = Search(eldap:substrings("sn", [{final, "r"}])), F_AND = eldap:'and'([eldap:present("objectclass"), eldap:present("ou")]), {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) -> Mod = [eldap:mod_replace("telephoneNumber", ["555-12345"]), eldap:mod_add("description", ["Nice guy"])], %% io:format("MOD ~p ~p ~n",[FB, Mod]), ok = eldap:modify(H, FB, Mod), %% 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, [{"objectclass", ["person"]}, {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]), ok = eldap:delete(H, "cn=Jonas Jonsson," ++ BasePath), {error, noSuchObject} = eldap:delete(H, "cn=Jonas Jonsson," ++ BasePath). chk_modify_dn(H, FB) -> ok = eldap:modify_dn(H, FB, "cn=Niclas Andre", true, ""). %%io:format("Res ~p~n ~p~n",[R, All(BasePath)]). %%%---------------- add(H, Attr, Value, Path0, Attrs, Class) -> Path = case Path0 of [] -> Attr ++ "=" ++ Value; _ -> Attr ++ "=" ++ Value ++ "," ++ Path0 end, case eldap:add(H, Path, [{"objectclass", Class}, {Attr, [Value]}] ++ Attrs) of ok -> {ok, Path}; {error, E = entryAlreadyExists} -> {E, Path}; R = {error, Reason} -> io:format("~p:~p: ~s,~s =>~n ~p~n", [?MODULE,?LINE, Attr, Value, R]), exit({ldap, add, Reason}) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Develop %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% test() -> run(). run() -> Cases = all(), run(Cases). run(Case) when is_atom(Case) -> run([Case]); run(Cases) when is_list(Cases) -> Run = fun(Test, Config0) -> Config = init_per_testcase(Test, Config0), try io:format("~nTest ~p ... ",[Test]), ?MODULE:Test(Config), end_per_testcase(Test, Config), io:format("ok~n",[]) catch _:Reason -> io:format("~n FAIL (~p): ~p~n ~p~n", [Test, Reason, erlang:get_stacktrace()]) end end, process_flag(trap_exit, true), Pid = spawn_link(fun() -> case init_per_suite([]) of {skip, Reason} -> io:format("Skip ~s~n",[Reason]); Config -> try [Run(Test, Config) || Test <- Cases] catch _:Err -> io:format("Error ~p in ~p~n",[Err, erlang:get_stacktrace()]) end, end_per_suite(Config) end end), receive {'EXIT', Pid, normal} -> ok; Msg -> io:format("Received ~p (~p)~n",[Msg, Pid]) after 100 -> ok end, process_flag(trap_exit, false), ok.