From 445647da64872f1728c7dc180542b693e7bf866e Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Fri, 19 Feb 2016 16:08:30 +0100 Subject: eldap: referral resultCode --- lib/eldap/doc/src/eldap.xml | 97 ++++++++++++++++++++++++++---------- lib/eldap/src/eldap.erl | 68 ++++++++++++++++++------- lib/eldap/test/eldap_basic_SUITE.erl | 94 ++++++++++++++++++++++++++++++---- 3 files changed, 206 insertions(+), 53 deletions(-) (limited to 'lib/eldap') diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml index 8f4479a730..70add6619d 100644 --- a/lib/eldap/doc/src/eldap.xml +++ b/lib/eldap/doc/src/eldap.xml @@ -4,7 +4,7 @@
- 20122013 + 20122016 Ericsson AB. All Rights Reserved. @@ -29,7 +29,7 @@ B
eldap - Eldap Functions + LDAP Client

This module provides a client api to the Lightweight Directory Access Protocol (LDAP).

@@ -40,20 +40,67 @@

The above publications can be found at IETF.

-

Types

-
-handle()    Connection handle
-attribute() {Type = string(), Values=[string()]}
-modify_op() See mod_add/2, mod_delete/2, mod_replace/2
-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, extensibleMatch/2,
-                'and'/1, 'or'/1, 'not'/1.
-    
-

+ +
+ DATA TYPES +

Type definitions that are used more than once in this module: +

+ + handle() +

Connection handle

+ + attribute() = +

{Type = string(), Values=[string()]}

+ + modify_op() +

See + mod_add/2, + mod_delete/2, + mod_replace/2 +

+ + 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, + extensibleMatch/2, + 'and'/1, + 'or'/1, + 'not'/1 +

+ + return_value() = +

ok | {ok, {referral,referrals()}} | {error,Error} +

+ + referrals() = +

[Address = string()] The contents of Address is server dependent. +

+ +
+
+ + open([Host]) -> {ok, Handle} | {error, Reason} @@ -92,14 +139,14 @@ filter() See present/1, substrings/2, - start_tls(Handle, Options) -> ok | {error,Error} + start_tls(Handle, Options) -> return_value() Upgrade a connection to TLS.

Same as start_tls(Handle, Options, infinity)

- start_tls(Handle, Options, Timeout) -> ok | {error,Error} + start_tls(Handle, Options, Timeout) -> return_value() Upgrade a connection to TLS. Handle = handle() @@ -128,7 +175,7 @@ filter() See present/1, substrings/2, - simple_bind(Handle, Dn, Password) -> ok | {error, Reason} + simple_bind(Handle, Dn, Password) -> return_value() Authenticate the connection. Handle = handle() @@ -140,7 +187,7 @@ filter() See present/1, substrings/2, - add(Handle, Dn, [Attribute]) -> ok | {error, Reason} + add(Handle, Dn, [Attribute]) -> return_value() Add an entry. Handle = handle() @@ -161,7 +208,7 @@ filter() See present/1, substrings/2, - delete(Handle, Dn) -> ok | {error, Reason} + delete(Handle, Dn) -> return_value() Delete an entry. Dn = string() @@ -203,7 +250,7 @@ filter() See present/1, substrings/2, - modify(Handle, Dn, [ModifyOp]) -> ok | {error, Reason} + modify(Handle, Dn, [ModifyOp]) -> return_value() Modify an entry. Dn = string() @@ -219,7 +266,7 @@ filter() See present/1, substrings/2, - modify_password(Handle, Dn, NewPasswd) -> ok | {ok, GenPasswd} | {error, Reason} + modify_password(Handle, Dn, NewPasswd) -> return_value() | {ok, GenPasswd} Modify the password of a user. Dn = string() @@ -230,7 +277,7 @@ filter() See present/1, substrings/2, - modify_password(Handle, Dn, NewPasswd, OldPasswd) -> ok | {ok, GenPasswd} | {error, Reason} + modify_password(Handle, Dn, NewPasswd, OldPasswd) -> return_value() | {ok, GenPasswd} Modify the password of a user. Dn = string() @@ -259,7 +306,7 @@ filter() See present/1, substrings/2, - modify_dn(Handle, Dn, NewRDN, DeleteOldRDN, NewSupDN) -> ok | {error, Reason} + modify_dn(Handle, Dn, NewRDN, DeleteOldRDN, NewSupDN) -> return_value() Modify the DN of an entry. Dn = string() @@ -279,7 +326,7 @@ filter() See present/1, substrings/2, - search(Handle, SearchOptions) -> {ok, #eldap_search_result{}} | {error, Reason} + search(Handle, SearchOptions) -> {ok, #eldap_search_result{}} | {ok, {referral,referrals()}} | {error, Reason} Search the Directory SearchOptions = #eldap_search{} | [SearchOption] diff --git a/lib/eldap/src/eldap.erl b/lib/eldap/src/eldap.erl index ae47c815c9..8b73860a6d 100644 --- a/lib/eldap/src/eldap.erl +++ b/lib/eldap/src/eldap.erl @@ -19,7 +19,9 @@ 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, + mod_replace/2, add/3, + delete/2, delete/3, + modify_dn/5,parse_dn/1, parse_ldap_url/1]). -export([neverDerefAliases/0, derefInSearching/0, @@ -188,7 +190,10 @@ add_attrs(Attrs) -> %%% ) %%% -------------------------------------------------------------------- delete(Handle, Entry) when is_pid(Handle), is_list(Entry) -> - send(Handle, {delete, Entry}), + delete(Handle, Entry, asn1_NOVALUE). + +delete(Handle, Entry, Controls) when is_pid(Handle), is_list(Entry) -> + send(Handle, {delete, Entry, Controls}), recv(Handle). %%% -------------------------------------------------------------------- @@ -504,8 +509,8 @@ loop(Cpid, Data) -> send(From,Res), ?MODULE:loop(Cpid, NewData); - {From, {delete, Entry}} -> - {Res,NewData} = do_delete(Data, Entry), + {From, {delete, Entry, Controls}} -> + {Res,NewData} = do_delete(Data, Entry, Controls), send(From,Res), ?MODULE:loop(Cpid, NewData); @@ -593,8 +598,9 @@ do_start_tls(Data=#eldap{fd=FD} , TlsOptions, Timeout) -> {error,Error} -> {{error,Error}, Data} end; - {error,Error} -> {{error,Error},Data}; - Else -> {{error,Else},Data} + {{ok,Val},NewData} -> {{ok,Val},NewData}; + {error,Error} -> {{error,Error},Data}; + Else -> {{error,Else},Data} end. -define(START_TLS_OID, "1.3.6.1.4.1.1466.20037"). @@ -611,6 +617,8 @@ exec_extended_req_reply(Data, {ok,Msg}) when case Result#'ExtendedResponse'.resultCode of success -> {ok,Data}; + referral -> + {{ok, {referral,Result#'ExtendedResponse'.referral}}, Data}; Error -> {error, {response,Error}} end; @@ -639,9 +647,10 @@ do_the_simple_bind(Data, Dn, Passwd) -> case catch exec_simple_bind(Data#eldap{binddn = Dn, passwd = Passwd, id = bump_id(Data)}) of - {ok,NewData} -> {ok,NewData}; - {error,Emsg} -> {{error,Emsg},Data}; - Else -> {{error,Else},Data} + {ok,NewData} -> {ok,NewData}; + {{ok,Val},NewData} -> {{ok,Val},NewData}; + {error,Emsg} -> {{error,Emsg},Data}; + Else -> {{error,Else},Data} end. exec_simple_bind(Data) -> @@ -659,6 +668,7 @@ exec_simple_bind_reply(Data, {ok,Msg}) when {bindResponse, Result} -> case Result#'BindResponse'.resultCode of success -> {ok,Data}; + referral -> {{ok, {referral,Msg#'BindResponse'.referral}}, Data}; Error -> {error, Error} end; Other -> {error, Other} @@ -675,6 +685,7 @@ do_search(Data, A) -> case catch do_search_0(Data, A) of {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data}; {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data}; + {{ok,Val},NewData} -> {{ok,Val},NewData}; {ok,Res,Ref,NewData} -> {{ok,polish(Res, Ref)},NewData}; {{error,Reason},NewData} -> {{error,Reason},NewData}; Else -> {ldap_closed_p(Data, Else),Data} @@ -732,6 +743,8 @@ collect_search_responses(Data, S, ID, {ok,Msg}, Acc, Ref) success -> log2(Data, "search reply = searchResDone ~n", []), {ok,Acc,Ref,Data}; + referral -> + {{ok, {referral,R#'LDAPResult'.referral}}, Data}; Reason -> {{error,Reason},Data} end; @@ -761,6 +774,7 @@ do_add(Data, Entry, Attrs) -> {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data}; {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data}; {ok,NewData} -> {ok,NewData}; + {{ok,Val},NewData} -> {{ok,Val},NewData}; Else -> {ldap_closed_p(Data, Else),Data} end. @@ -779,19 +793,20 @@ do_add_0(Data, Entry, Attrs) -> %%% deleteRequest %%% -------------------------------------------------------------------- -do_delete(Data, Entry) -> - case catch do_delete_0(Data, Entry) of +do_delete(Data, Entry, Controls) -> + case catch do_delete_0(Data, Entry, Controls) of {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data}; {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data}; {ok,NewData} -> {ok,NewData}; + {{ok,Val},NewData} -> {{ok,Val},NewData}; Else -> {ldap_closed_p(Data, Else),Data} end. -do_delete_0(Data, Entry) -> +do_delete_0(Data, Entry, Controls) -> S = Data#eldap.fd, Id = bump_id(Data), log2(Data, "del request = ~p~n", [Entry]), - Resp = request(S, Data, Id, {delRequest, Entry}), + Resp = request(S, Data, Id, {delRequest, Entry, Controls}), log2(Data, "del reply = ~p~n", [Resp]), check_reply(Data#eldap{id = Id}, Resp, delResponse). @@ -805,6 +820,7 @@ do_modify(Data, Obj, Mod) -> {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data}; {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data}; {ok,NewData} -> {ok,NewData}; + {{ok,Val},NewData} -> {{ok,Val},NewData}; Else -> {ldap_closed_p(Data, Else),Data} end. @@ -830,6 +846,7 @@ do_passwd_modify(Data, Dn, NewPasswd, OldPasswd) -> {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data}; {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data}; {ok,NewData} -> {ok,NewData}; + {{ok,Val},NewData} -> {{ok,Val},NewData}; {ok,Passwd,NewData} -> {{ok, Passwd},NewData}; Else -> {ldap_closed_p(Data, Else),Data} end. @@ -865,6 +882,8 @@ exec_passwd_modify_reply(Data, {ok,Msg}) when throw(Error) end end; + referral -> + {{ok, {referral,Result#'ExtendedResponse'.referral}}, Data}; Error -> {error, {response,Error}} end; @@ -882,6 +901,7 @@ do_modify_dn(Data, Entry, NewRDN, DelOldRDN, NewSup) -> {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data}; {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data}; {ok,NewData} -> {ok,NewData}; + {{ok,Val},NewData} -> {{ok,Val},NewData}; Else -> {ldap_closed_p(Data, Else),Data} end. @@ -900,15 +920,26 @@ do_modify_dn_0(Data, Entry, NewRDN, DelOldRDN, NewSup) -> %%% -------------------------------------------------------------------- %%% Send an LDAP request and receive the answer %%% -------------------------------------------------------------------- - request(S, Data, ID, Request) -> send_request(S, Data, ID, Request), recv_response(S, Data). -send_request(S, Data, ID, Request) -> - Message = #'LDAPMessage'{messageID = ID, - protocolOp = Request}, - {ok,Bytes} = 'ELDAPv3':encode('LDAPMessage', Message), +send_request(S, Data, Id, {T,P}) -> + send_the_LDAPMessage(S, Data, #'LDAPMessage'{messageID = Id, + protocolOp = {T,P}}); +send_request(S, Data, Id, {T,P,asn1_NOVALUE}) -> + send_the_LDAPMessage(S, Data, #'LDAPMessage'{messageID = Id, + protocolOp = {T,P}}); +send_request(S, Data, Id, {T,P,Controls0}) -> + Controls = [#'Control'{controlType=F1, + criticality=F2, + controlValue=F3} || {control,F1,F2,F3} <- Controls0], + send_the_LDAPMessage(S, Data, #'LDAPMessage'{messageID = Id, + protocolOp = {T,P}, + controls = Controls}). + +send_the_LDAPMessage(S, Data, LDAPMessage) -> + {ok,Bytes} = 'ELDAPv3':encode('LDAPMessage', LDAPMessage), case do_send(S, Data, Bytes) of {error,Reason} -> throw({gen_tcp_error,Reason}); Else -> Else @@ -942,6 +973,7 @@ check_reply(Data, {ok,Msg}, Op) when {Op, Result} -> case Result#'LDAPResult'.resultCode of success -> {ok,Data}; + referral -> {{ok, {referral,Result#'LDAPResult'.referral}}, Data}; Error -> {error, Error} end; Other -> {error, Other} diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl index 638a609346..8414ca6e46 100644 --- a/lib/eldap/test/eldap_basic_SUITE.erl +++ b/lib/eldap/test/eldap_basic_SUITE.erl @@ -30,6 +30,11 @@ -define(TIMEOUT, 120000). % 2 min + +%% Control to delete a referral object: +-define(manageDsaIT, {control,"2.16.840.1.113730.3.4.2",false,asn1_NOVALUE}). + + all() -> [app, appup, @@ -59,6 +64,7 @@ groups() -> {api_bound, [], [add_when_bound, add_already_exists, more_add, + add_referral, search_filter_equalityMatch, search_filter_substring_any, search_filter_initial, @@ -67,8 +73,11 @@ groups() -> search_filter_or, search_filter_and_not, search_two_hits, + search_referral, modify, + modify_referral, delete, + delete_referral, modify_dn_delete_old, modify_dn_keep_old]}, {v4_connections, [], connection_tests()}, @@ -92,11 +101,16 @@ connection_tests() -> init_per_suite(Config) -> SSL_available = init_ssl_certs_et_al(Config), - LDAP_server = find_first_server(false, [{config,eldap_server}, {config,ldap_server}, {"localhost",9876}]), + LDAP_server = find_first_server(false, [{config,eldap_server}, + {config,ldap_server}, + {"localhost",9876}, + {"aramis.otp.ericsson.se",9876}]), LDAPS_server = case SSL_available of true -> - find_first_server(true, [{config,ldaps_server}, {"localhost",9877}]); + find_first_server(true, [{config,ldaps_server}, + {"localhost",9877}, + {"aramis.otp.ericsson.se",9877}]); false -> undefined end, @@ -454,6 +468,16 @@ more_add(Config) -> [{"objectclass", ["organizationalUnit"]}, {"ou", ["Team"]}]). +%%%---------------------------------------------------------------- +add_referral(Config) -> + H = ?config(handle, Config), + BasePath = ?config(eldap_path, Config), + {ok,{referral,["ldap://nowhere.example.com"++_]}} = + eldap:add(H, "cn=Foo Bar,dc=notHere," ++ BasePath, + [{"objectclass", ["person"]}, + {"cn", ["Foo Bar"]}, + {"sn", ["Bar"]}, + {"telephoneNumber", ["555-1232", "555-5432"]}]). %%%---------------------------------------------------------------- search_filter_equalityMatch(Config) -> @@ -568,6 +592,16 @@ search_two_hits(Config) -> %% Restore the database: [ok=eldap:delete(H,DN) || DN <- ExpectedDNs]. +%%%---------------------------------------------------------------- +search_referral(Config) -> + H = ?config(handle, Config), + BasePath = ?config(eldap_path, Config), + DN = "cn=Santa Claus,dc=notHere," ++ BasePath, + {ok,{referral,["ldap://nowhere.example.com"++_]}} = + eldap:search(H, #eldap_search{base = DN, + filter = eldap:present("description"), + scope=eldap:singleLevel()}). + %%%---------------------------------------------------------------- modify(Config) -> H = ?config(handle, Config), @@ -601,6 +635,19 @@ modify(Config) -> %% restore the orignal version: restore_original_object(H, DN, OriginalAttrs). +%%%---------------------------------------------------------------- +modify_referral(Config) -> + H = ?config(handle, Config), + BasePath = ?config(eldap_path, Config), + %% The object to modify + DN = "cn=Foo Bar,dc=notHere," ++ BasePath, + + %% Do a change + Mod = [eldap:mod_replace("telephoneNumber", ["555-12345"]), + eldap:mod_add("description", ["Nice guy"])], + {ok,{referral,["ldap://nowhere.example.com"++_]}} = + eldap:modify(H, DN, Mod). + %%%---------------------------------------------------------------- delete(Config) -> H = ?config(handle, Config), @@ -619,6 +666,14 @@ delete(Config) -> %% And restore the object for subsequent tests restore_original_object(H, DN, OriginalAttrs). +%%%---------------------------------------------------------------- +delete_referral(Config) -> + H = ?config(handle, Config), + BasePath = ?config(eldap_path, Config), + %% The element to play with: + DN = "cn=Jonas Jonsson,dc=notHere," ++ BasePath, + {ok,{referral,["ldap://nowhere.example.com"++_]}} = eldap:delete(H, DN). + %%%---------------------------------------------------------------- modify_dn_delete_old(Config) -> H = ?config(handle, Config), @@ -817,25 +872,44 @@ delete_old_contents(H, Path) -> {filter, eldap:present("objectclass")}, {scope, eldap:wholeSubtree()}]) of - {ok, #eldap_search_result{entries=Entries}} -> + {ok, _R=#eldap_search_result{entries=Entries}} -> + case eldap:delete(H, "dc=notHere,"++Path, [?manageDsaIT]) of + ok -> ok; + {error,noSuchObject} -> ok; + Other -> ct:fail("eldap:delete notHere ret ~p",[Other]) + end, [ok = eldap:delete(H,DN) || #eldap_entry{object_name=DN} <- Entries]; _Res -> ignore end. + +-define(ok(X), ok(?MODULE,?LINE,X)). + add_new_contents(H, Path, MyHost) -> - ok(eldap:add(H,"dc=ericsson,dc=se", + ?ok(eldap:add(H,"dc=ericsson,dc=se", [{"objectclass", ["dcObject", "organization"]}, {"dc", ["ericsson"]}, {"o", ["Testing"]}])), - ok(eldap:add(H,Path, + ?ok(eldap:add(H,Path, [{"objectclass", ["dcObject", "organization"]}, {"dc", [MyHost]}, - {"o", ["Test machine"]}])). - - -ok({error,entryAlreadyExists}) -> ok; -ok(X) -> ok=X. + {"o", ["Test machine"]}])), + ?ok(eldap:add(H, "dc=notHere,"++Path, + [{"objectclass", ["referral", + "dcObject" + ]}, + {"ref", ["ldap://nowhere.example.com/notHere,"++Path]}, + {"dc", ["notHere"]} + ])). + + + +ok(_, _, {error,entryAlreadyExists}) -> ok; +ok(_, _, ok) -> ok; +ok(MODULE, LINE, X) -> + ct:pal("~p:~p add_new_contents: ret from eldap:add = ~p",[MODULE,LINE,X]), + X. -- cgit v1.2.3