📄 eldap.erl
字号:
-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鰎n T鰎nkvist, 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: eldap.erl,v 1.2 2004/07/23 14:24:09 aleksey Exp $ ').%%%----------------------------------------------------------------------%%% 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}.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -