Oracle® Fusion Middleware Administrator's Guide for Oracle Internet Directory 11g Release 1 (11.1.1) E10029-02 |
|
![]() Previous |
![]() Next |
This appendix explains how to use the plug-in framework in PL/SQL.
This appendix contains these topics:
This section contains these topics:
The following caveats apply to PL/SQL plug-ins:
A PL/SQL plug-in can only be associated with ldapbind
, ldapadd
, ldapmodify
, ldapcompare
, ldapsearch
, and ldapdelete
operations. You cannot associate a PL/SQL plug-in with moddn
. If you must associate a plug-in with moddn
, you must use a Java plug-in.
Plug-in names (PL/SQL package names) must be unique if they share the same database schema with other plug-ins or stored procedures. But plug-ins can share names with other database schema objects such as tables and views. This kind of sharing is not, however, recommended.
Creating a PL/SQL plug-in module is like creating a PL/SQL package. Both have a specification part and a body part. The directory, not the plug-in, defines the plug-in specification because the specification serves as the interface between Oracle Internet Directory and the custom plug-in.
For security reasons and for the integrity of the LDAP server, you can compile PL/SQL plug-ins only in the ODS database schema. You must compile them in the database that serves as the back end database of Oracle Internet Directory.
Different plug-ins have different package specifications. As Table F-1 shows, you can name the plug-in package. You must, however, follow the signatures defined for each type of plug-in procedure. See "Specifications for PL/SQL Plug-in Procedures" for details.
Table F-1 Plug-in Module Interface
Plug-in Item | User Defined | Oracle Internet Directory-Defined |
---|---|---|
Plug-in Package Name |
X |
|
Plug-in Procedure Name |
X |
|
Plug-in Procedure Signature |
X |
Table F-2 names the different plug-in procedures. In addition, it lists and describes the parameters that these procedures use.
Table F-2 Operation-Based and Attribute-Based Plug-in Procedure Signatures
Invocation Context | Procedure Name | IN Parameters | OUT Parameters |
---|---|---|---|
Before |
PRE_BIND |
ldapcontext, Bind DN, Password |
return code, error message |
With |
WHEN_BIND_REPLACE |
ldapcontext, bind result, DN, userpassword |
bind result, return code, error message |
After |
POST_BIND |
ldapcontext, Bind result, Bind DN, Password |
return code, error message |
Before |
PRE_MODIFY |
ldapcontext, DN, Mod structure |
return code, error message |
With |
WHEN_MODIFY |
ldapcontext, DN, Mod structure |
return code, error message |
With |
WHEN_MODIFY_REPLACE |
ldapcontext, DN, Mod structure |
return code, error message |
After |
POST_MODIFY |
ldapcontext, Modify result, DN, Mod structure |
return code, error message |
Before |
PRE_COMPARE |
ldapcontext, DN, attribute, value |
return code, error message |
With |
WHEN_COMPARE_REPLACE |
ldapcontext, Compare result, DN, attribute, value |
compare result, return code, error message |
After |
POST_COMPARE |
ldapcontext, Compare result, DN, attribute, value |
return code, error message |
Before |
PRE_ADD |
ldapcontext, DN, Entry |
return code, error message |
With |
WHEN_ADD |
ldapcontext, DN, Entry |
return code, error message |
With |
WHEN_ADD_REPLACE |
ldapcontext, DN, Entry |
return code, error message |
After |
POST_ADD |
ldapcontext, Add result, DN, Entry |
return code, error message |
Before |
PRE_DELETE |
ldapcontext, DN |
return code, error message |
With |
WHEN_DELETE |
ldapcontext, DN |
return code, error message |
With |
WHEN_DELETE |
ldapcontext, DN |
return code, error message |
After |
POST_DELETE |
ldapcontext, Delete result, DN |
return code, error message |
Before |
PRE_SEARCH |
ldapcontext, Base DN, scope, filter |
return code, error message |
After |
POST_SEARCH |
Ldap context, Search result, Base DN, scope, filter |
return code, error message |
See Also:
|
You must compile the plug-in module against the same database that serves as the Oracle Internet Directory back end database. Plug-ins are the same as PL/SQL stored procedures. A PL/SQL anonymous block is compiled each time it is loaded into memory. Compilation consists of these stages:
Syntax checking: PL/SQL syntax is checked, and a parse tree is generated.
Semantic checking: Type checking and further processing on the parse tree.
Code generation: The pcode is generated.
If errors occur during the compilation of a plug-in, the plug-in is not created. You can use the SHOW ERRORS
statement in SQL*Plus or Enterprise Manager to see any compilation errors when you create a plug-in, or you can SELECT
the errors from the USER_ERRORS
view.
All plug-in modules must be compiled in the ODS database schema.
Compiled plug-ins have dependencies. They become invalid if an object depended upon, such as a stored procedure or function called from the plug-in body, is modified. Plug-ins that are invalidated for dependency reasons must be recompiled before the next invocation.
This section explains how to modify and debug plug-ins.
Like a stored procedure, a plug-in cannot be explicitly altered. It must be replaced with a new definition.
When replacing a plug-in, you must include the OR REPLACE
option in the CREATE PACKAGE
statement. The OR REPLACE
option enables a new version of an existing plug-in to replace an older version without having an effect on grants made for the original version of the plug-in.
Alternatively, the plug-in can be dropped using the DROP PACKAGE
statement, and you can rerun the CREATE PACKAGE
statement.
If the plug-in name (the package name) is changed, you must register the new plug-in again.
To turn the plug-in on or off, modify the value of orclPluginEnable
in the plug-in configuration object. For example, modify the value of orclPluginEnable
in cn=post_mod_plugin,cn=plugins,cn=subconfigsubentry
to be 1
or 0
.
Each of the procedures in a PL/SQL plug-in must have an exception handling block that handles errors intelligently and, if possible, recovers from them.
Oracle Internet Directory requires that the return code (rc
) and error message (errmsg
) be set correctly in the plug-in procedures.
Table F-3 provides the values that are valid for the return code.
Table F-3 Valid Values for the plug-in Return Code
Error Code | Description |
---|---|
0 |
Success |
Any number greater than zero |
Failure |
-1 |
Warning |
The errmsg
parameter is a string value that can pass a user's custom error message back to Oracle Internet Directory server. The size limit for errmsg
is 1024 bytes. Each time Oracle Internet Directory runs the plug-in program, it examines the return code to determine if it must display the error message.
If, for example, the value for the return code is 0
, the error message value is ignored. If the value of the return code is -1
or greater than zero, the following message is either logged in the log file or displayed in standard output if the request came from LDAP command-line tools:
ldap addition info: customized error
Table F-4 shows where plug-in exceptions occur and how the directory handles them.
Table F-4 Program Control Handling when a Plug-in Exception Occurs
Plug-in Exception Occurred in | Oracle Internet Directory Server Handling |
---|---|
|
Depends on return code. If the return code is:
|
|
LDAP operation is completed. There is no rollback. |
|
Rollback the LDAP operation |
Table F-5 shows how the directory responds when an LDAP operation fails.
Table F-5 Program Control Handling when an LDAP Operation Fails
LDAP Operation Fails in | Oracle Internet Directory Server Handling |
---|---|
|
Pre-operation plug-in is completed. There is no rollback. |
|
Proceed with post-operation plug-in. The LDAP operation result is one of the |
|
When types of plug-in changes are rolled back. |
|
Changes made in the plug-in program body are rolled back. |
There are different methods for providing API access:
Enable a user to utilize the standard LDAP PL/SQL APIs. Note though that, if program logic is not carefully planned, an infinite loop in plug-in execution can result.
Oracle Internet Directory provides the Plug-in LDAP API. This plug-in does not cause a series of plug-in actions in the directory server if there are plug-ins configured and associated with the LDAP request.
In the Plug-in LDAP API, the directory provides APIs for connecting back to the directory server designated in the plug-in module. You must use this API if you want to connect to the server that is executing the plug-in. If you want to connect to an external server, you can use the DBMS_LDAP API.
Within each plug-in module, an ldapcontext
is passed from the Oracle directory server. When the Plug-in LDAP API is called, ldapcontext
is passed for security and binding purposes. When binding with this ldapcontext
, Oracle Internet Directory recognizes that the LDAP request is coming from a plug-in module. For this type of plug-in bind, the directory does not trigger any subsequent plug-ins. It handles the plug-in bind as a super-user bind. Use this plug-in bind with discretion.
Bulk tools do not support server plug-ins.
Some Oracle Internet Directory server plug-ins require that you supply the code that preserves tight security. For example, if you replace the directory's ldapcompare
or ldapbind
operation with your own plug-in module, you must ensure that your implementation of this operation does not omit any functionality on which security relies.
To ensure tight security, the following must be done:
Create the plug-in packages
Only the LDAP administrator can restrict the database user
Use the access control list (ACL) to set the plug-in configuration entries to be accessed only by the LDAP administrator
Be aware of the program relationship between different plug-ins
Use the plug-in debugging mechanism for Oracle Internet Directory to examine the process and content of plug-ins.The following commands control the operation of the server debugging process.
To set up plug-in debugging, run this command:
% sqlplus ods @$ORACLE/ldap/admin/oidspdsu.pls
To enable plug-in debugging, run this command:
% sqlplus ods @$ORACLE/ldap/admin/oidspdon.pls
After enabling plug-in debugging, you can use this command in the plug-in module code:
plg_debug('debuggingmessage');
The resulting debug message is stored in the plug-in debugging table.
To disable debugging, run this command:
% sqlplus ods @$ORACLE/ldap/admin/oidspdof.pls
To display the debug messages that you put in the plug-in module, run this command:
% sqlplus ods @$ORACLE/ldap/admin/oidspdsh.pls
To delete all of the debug messages from the debug table, run this command:
% sqlplus ods @$ORACLE/ldap/admin/oidspdde.pls
Here is the package specification that Oracle Internet Directory provides for the PL/SQL Plug-in LDAP API:
CREATE OR REPLACE PACKAGE LDAP_PLUGIN AS SUBTYPE SESSION IS RAW(32); -- Initializes the LDAP library and return a session handler -- for use in subsequent calls. FUNCTION init (ldappluginctx IN ODS.plugincontext) RETURN SESSION; -- Synchronously authenticates to the directory server using -- a Distinguished Name and password. FUNCTION simple_bind_s (ldappluginctx IN ODS.plugincontext, ld IN SESSION) RETURN PLS_INTEGER; -- Get requester info from the plug-in context FUNCTION get_requester (ldappluginctx IN ODS.plugincontext) RETURN VARCHAR2; END LDAP_PLUGIN;
Oracle Internet Directory can use several different versions of the Oracle Database for storing directory data. These include Oracle Database Release 2, v9.2.0.6 or later and Oracle Database 10g, v10.1.0.4 or later.
The following plug-in features are not supported in the directory server running against Oracle Database Release 2:
Windows Domain external authentication plug-in.
The simple_bind_s()
function of the LDAP_PLUGIN package provided as the Oracle Internet Directory PL/SQL PLUGIN API for connecting back to the directory server as part of plug-in definitions.
This section presents two sample plug-ins. One logs all ldapsearch
commands. The other synchronizes two directory information trees (DITs).
Situation: A user wants to know if it is possible to log all of the ldapsearch
commands.
Solution: Yes. The user can use the post ldapsearch
configuration plug-in for this purpose. They can either log all of the requests or only those that occur under the DNs being searched.
To log all the ldapsearch
commands:
Log all of the ldapsearch
results into a database table. This log table has these columns:
timestamp
baseDN
search scope
search filter
required attribute
search result
Use this SQL script to create the table:
drop table search_log; create table search_log (timestamp varchar2(50), basedn varchar2(256), searchscope number(1); searchfilter varchar2(256); searchresult number(1)); drop table simple_tab; create table simple_tab (id NUMBER(7), dump varchar2(256)); DROP sequence seq; CREATE sequence seq START WITH 10000; commit;
Create the plug-in package specification.
CREATE OR REPLACE PACKAGE LDAP_PLUGIN_EXAMPLE1 AS PROCEDURE post_search (ldapplugincontext IN ODS.plugincontext, result IN INTEGER, baseDN IN VARCHAR2, scope IN INTEGER, filterStr IN VARCHAR2, requiredAttr IN ODS.strCollection, rc OUT INTEGER, errormsg OUT VARCHAR2 ); END LDAP_PLUGIN_EXAMPLE1; /
Create the plug-in package body.
CREATE OR REPLACE PACKAGE BODY LDAP_PLUGIN_EXAMPLE1 AS PROCEDURE post_search (ldapplugincontext IN ODS.plugincontext, result IN INTEGER, baseDN IN VARCHAR2, scope IN INTEGER, filterStr IN VARCHAR2, requiredAttr IN ODS.strCollection, rc OUT INTEGER, errormsg OUT VARCHAR2 ) IS BEGIN INSERT INTO simple_tab VALUES (to_char(sysdate, 'Month DD, YYYY HH24:MI:SS'), baseDN, scope, filterStr, result); -- The following code segment demonstrate how to iterate -- the ODS.strCollection FOR l_counter1 IN 1..requiredAttr.COUNT LOOP INSERT INTO simple_tab values (seq.NEXTVAL, 'req attr ' || l_counter1 || ' = ' || requiredAttr(l_counter1)); END LOOP; rc := 0; errormsg := 'no post_search plug-in error msg'; COMMIT; EXCEPTION WHEN others THEN rc := 1; errormsg := 'exception: post_search plug-in'; END; END LDAP_PLUGIN_EXAMPLE1; /
Register the plug-in entry in Oracle Internet Directory.
dn: cn=post_search,cn=plugin,cn=subconfigsubentry objectclass: orclPluginConfig objectclass: top orclPluginName: ldap_plugin_example1 orclPluginType: configuration orclPluginTiming: post orclPluginLDAPOperation: ldapsearch orclPluginEnable: 1 orclPluginVersion: 1.0.1 cn: post_search orclPluginKind: PLSQL
Using the ldapadd
command-line tool to add this entry:
% ldapadd –p port_number –h host_name –D bind_dn –q –v \ –f register_post_search.ldif
Situation: There are two interdependent products under cn=Products
, cn=oraclecontext
. This interdependency extends down to the users in these products' containers. If a user in the first DIT (product 1) is deleted, the corresponding user in the other DIT (product 2) must be deleted.
Is it possible to set a trigger that, when the user in the first DIT is deleted, calls or passes a trigger to delete the user in the second DIT?
Solution: Yes, we can use the post ldapdelete
operation plug-in to handle the second deletion occurring in the second DIT.
If the first DIT has the naming context of cn=DIT1,cn=products,cn=oraclecontext
and the second DIT has the naming context of cn=DIT2,cn=products,cn=oraclecontext
, the two users share the same ID attribute. Inside of the post ldapdelete
plug-in module, we can use LDAP_PLUGIN
and DBMS_LDAP
APIs to delete the user in the second DIT.
We must set orclPluginSubscriberDNList
to cn=DIT1,cn=products,cn=oraclecontext
, so that whenever we delete entries under cn=DIT1,cn=products,cn=oraclecontext
, the plug-in module is invoked.
Note: When you use a postldapmodify plug-in to synchronize changes between two Oracle Internet Directory nodes, you cannot push all the attributes from one node to the other. This is because the changes (mod structure) captured in the plug-in module include operational attributes. These operational attributes are generated on each node and cannot be modified by using the standard LDAP methods.
When writing your plug-in program, exclude the following operational attributes from synchronization: |
Assume that the entries under both DITs have been added to the directory. For example, the entry id=12345,cn=DIT1,cn=products,cn=oraclecontext
is in DIT1
, and id=12345,cn=DIT2,cn=products,cn=oraclecontext
is in DIT2
.
Create the plug-in package specification.
CREATE OR REPLACE PACKAGE LDAP_PLUGIN_EXAMPLE2 AS PROCEDURE post_delete (ldapplugincontext IN ODS.plugincontext, result IN INTEGER, dn IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 ); END LDAP_PLUGIN_EXAMPLE2; /
Create the plug-in package body.
CREATE OR REPLACE PACKAGE BODY LDAP_PLUGIN_EXAMPLE2 AS PROCEDURE post_delete (ldapplugincontext IN ODS.plugincontext, result IN INTEGER, dn IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 ) IS retval PLS_INTEGER; my_session DBMS_LDAP.session; newDN VARCHAR2(256); BEGIN retval := -1; my_session := LDAP_PLUGIN.init(ldapplugincontext); -- bind to the directory retval := LDAP_PLUGIN.simple_bind_s(ldapplugincontext, my_session); -- if retval is not 0, then raise exception newDN := REPLACE(dn,'DIT1','DIT2'); retval := DBMS_LDAP.delete_s(my_session, newDN); -- if retval is not 0, then raise exception rc := 0; errormsg := 'no post_delete plug-in error msg'; EXCEPTION WHEN others THEN rc := 1; errormsg := 'exception: post_delete plug-in'; END; END LDAP_PLUGIN_EXAMPLE2; / (ldapplugincontext IN ODS.plugincontext, result IN INTEGER, dn IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 ) IS retval PLS_INTEGER; my_session DBMS_LDAP.session; newDN VARCHAR2(256); BEGIN retval := -1; my_session := LDAP_PLUGIN.init(ldapplugincontext); -- bind to the directory retval := LDAP_PLUGIN.simple_bind_s(ldapplugincontext, my_session); -- if retval is not 0, then raise exception newDN := REPLACE(dn,'DIT1','DIT2'); retval := DBMS_LDAP.delete_s(my_session, newDN); -- if retval is not 0, then raise exception rc := 0; errormsg := 'no post_delete plug-in error msg'; EXCEPTION WHEN others THEN rc := 1; errormsg := 'exception: post_delete plug-in'; END; END LDAP_PLUGIN_EXAMPLE2; /
Register the plug-in entry with Oracle Internet Directory.
Construct the LDIF file register_post_delete.ldif
:
dn: cn=post_delete,cn=plugin,cn=subconfigsubentry objectclass: orclPluginConfig objectclass: top orclPluginName: ldap_plugin_example2 orclPluginType: configuration orclPluginTiming: post orclPluginLDAPOperation: ldapdelete orclPluginEnable: 1 orclPluginSubscriberDNList: cn=DIT1,cn=oraclecontext,cn=products orclPluginVersion: 1.0.1 cn: post_delete orclPluginKind: PLSQL
Use the ldapadd
command-line tool to add this entry:
% ldapadd –p port_number –h host_name –D bind_dn –q –v –f register_ post_delete.ldif
Starting with release 10.1.2, object definitions in the Plug-in LDAP API enable ldapmodify
, ldapadd
, and ldapcompare
plug-ins to access binary attributes in the directory database. Formerly, only attributes of type VARCHAR2
could be accessed. These object definitions do not invalidate plug-in code that precedes release 10.1.2. No change to this code is required. The new definitions appear in the section "Database Object Types Defined".
The section that you are reading now examines binary operations involving the three types of plug-ins. It includes examples of these plug-ins. The new object definitions apply to pre, post, and when versions of all three.
Note that the three examples use RAW functions and variables in place of LOBs.
The modobj
object that the plug-in framework passes to an ldapmodify
plug-in now holds the values of binary attributes as binvals
. This variable is a table of binvalobj
objects.
The plug-in determines whether a binary operation is being performed by examining the operation
field of modobj
. It checks whether any of the values DBMS_LDAP.MOD_ADD
, DBMS_LDAP.MOD_DELETE
, and DBMS_LDAP.MOD_REPLACE
are paired with DBMS_LDAP.MOD_BVALUES
. The pairing DBMS_LDAP.MOD_ADD+DBMS_LDAP.MOD_BVALUES
, for example, signifies a binary add in the modify operation.
The example that follows shows a post ldapmodify
plug-in modifying an entry in another directory. The plug-in is invoked after ldapmodify
applies the same change to the same entry in the plug-in directory. The entry in the other directory appears under the DIT cn=users,dc=us,dc=example,dc=com
.
create or replace package moduser as
procedure post_modify(ldapplugincontext IN ODS.plugincontext,
result IN integer,
dn IN varchar2,
mods IN ODS.modlist,
rc OUT integer,
errormsg OUT varchar2);
end moduser;
/
show error
CREATE OR REPLACE PACKAGE BODY moduser AS
procedure post_modify(ldapplugincontext IN ODS.plugincontext,
result IN integer,
dn IN varchar2,
mods IN ODS.modlist,
rc OUT integer,
errormsg OUT varchar2)
is
counter1 pls_integer;
counter2 pls_integer;
retval pls_integer := -1;
user_session DBMS_LDAP.session;
user_dn varchar(256);
user_array DBMS_LDAP.mod_array;
user_vals DBMS_LDAP.string_collection;
user_binvals DBMS_LDAP.blob_collection;
ldap_host varchar(256);
ldap_port varchar(256);
ldap_user varchar(256);
ldap_passwd varchar(256);
begin
ldap_host :='backup.us.example.com';
ldap_port :='4000';
ldap_user :='cn=orcladmin';
ldap_passwd :='password';
plg_debug('START MODIFYING THE ENTRY');
-- Get a session
user_session := dbms_ldap.init(ldap_host, ldap_port);
-- Bind to the directory
retval := dbms_ldap.simple_bind_s(user_session, ldap_user,
ldap_passwd);
-- Create a mod_array
user_array := dbms_ldap.create_mod_array(mods.count);
-- Create a user_dn
user_dn := substr(dn,1,instr(dn,',',1,1))||'cn=users,dc=us,dc=example,
dc=com';
plg_debug('THE CREATED DN IS'||user_dn);
-- Iterate through the modlist
for counter1 in 1..mods.count loop
-- Log the attribute name and operation
if (mods(counter1).operation > DBMS_LDAP.MOD_BVALUES) then
plg_debug('THE NAME OF THE BINARY ATTR. IS'||mods(counter1).type);
else
plg_debug('THE NAME OF THE NORMAL ATTR. IS'||mods(counter1).type);
end if;
plg_debug('THE OPERATION IS'||mods(counter1).operation);
-- Add the attribute values to the collection
for counter2 in 1..mods(counter1).vals.count loop
user_vals(counter2) := mods(counter1).vals(counter2).val;
end loop;
-- Add the attribute values to the collection
for counter2 in 1..mods(counter1).binvals.count loop
plg_debug('THE NO. OF BYTES OF THE BINARY ATTR. VALUE IS'
||mods(counter1).binvals(counter2).length);
user_binvals(counter2) := mods(counter1).binvals(counter2).binval;
end loop;
-- Populate the mod_array accordingly with binary/normal attributes
if (mods(counter1).operation >= DBMS_LDAP.MOD_BVALUES) then
dbms_ldap.populate_mod_array(user_array,mods(counter1).operation -
DBMS_LDAP.MOD_BVALUES,mods(counter1).type,user_binvals);
user_binvals.delete;
else
dbms_ldap.populate_mod_array(user_array,mods(counter1).operation,
mods(counter1).type,user_vals);
user_vals.delete;
end if;
end loop;
-- Modify the entry
retval := dbms_ldap.modify_s(user_session,user_dn,user_array);
if retval = 0 then
rc := 0;
errormsg:='No error occurred while modifying the entry';
else
rc := retval;
errormsg :='Error code'||rc||' while modifying the entry';
end if;
-- Free the mod_array
dbms_ldap.free_mod_array(user_array);
plg_debug('FINISHED MODIFYING THE ENTRY');
exception
WHEN others THEN
plg_debug (SQLERRM);
end;
end moduser;
/
show error
exit;
The entryobj
object that the plug-in framework passes to an ldapadd
plug-in now holds binary attributes as binattr
. This variable is a table of binattrobj
objects. The example that follows shows a post-add plug-in propagating a change (an added user) in the plug-in directory to another directory. In the latter directory, the entry appears under the DIT cn=users,dc=us,dc=example,dc=com
.
create or replace package adduser as
procedure post_add(ldapplugincontext IN ODS.plugincontext,
result IN integer,
dn IN varchar2,
entry IN ODS.entryobj,
rc OUT integer,
errormsg OUT varchar2);
end adduser;
/
show error
CREATE OR REPLACE PACKAGE BODY adduser AS
procedure post_add(ldapplugincontext IN ODS.plugincontext,
result IN integer,
dn IN varchar2,
entry IN ODS.entryobj,
rc OUT integer,
errormsg OUT varchar2)
is
counter1 pls_integer;
counter2 pls_integer;
retval pls_integer := -1;
s integer;
user_session DBMS_LDAP.session;
user_dn varchar(256);
user_array DBMS_LDAP.mod_array;
user_vals DBMS_LDAP.string_collection;
user_binvals DBMS_LDAP.blob_collection;
ldap_host varchar(256);
ldap_port varchar(256);
ldap_user varchar(256);
ldap_passwd varchar(256);
begin
ldap_host :='backup.us.example.com';
ldap_port :='4000';
ldap_user :='cn=orcladmin';
ldap_passwd :='password';
plg_debug('START ADDING THE ENTRY');
-- Get a session
user_session := dbms_ldap.init(ldap_host, ldap_port);
-- Bind to the directory
retval := dbms_ldap.simple_bind_s(user_session, ldap_user, ldap_passwd);
-- Create a mod_array
user_array := dbms_ldap.create_mod_array(entry.binattr.count +
entry.attr.count);
-- Create a user_dn
user_dn := substr(dn,1,instr(dn,',',1,1))||'cn=users,dc=us,dc=example,
dc=com';
plg_debug('THE CREATED DN IS'||user_dn);
-- Populate the mod_array with binary attributes
for counter1 in 1..entry.binattr.count loop
for counter2 in 1..entry.binattr(counter1).binattrval.count loop
plg_debug('THE NAME OF THE BINARY ATTR. IS'||
entry.binattr(counter1).binattrname);
s := dbms_lob.getlength(entry.binattr(counter1).
binattrval(counter2));
plg_debug('THE NO. OF BYTES OF THE BINARY ATTR. VALUE IS'||s);
user_binvals(counter2) := entry.binattr(counter1).
binattrval(counter2);
end loop;
dbms_ldap.populate_mod_array(user_array,DBMS_LDAP.MOD_ADD,
entry.binattr(counter1).binattrname,user_binvals);
user_binvals.delete;
end loop;
-- Populate the mod_array with attributes
for counter1 in 1..entry.attr.count loop
for counter2 in 1..entry.attr(counter1).attrval.count loop
plg_debug('THE NORMAL ATTRIBUTE'||entry.attr(counter1).attrname||'
HAS THE VALUE'||entry.attr(counter1).attrval(counter2));
user_vals(counter2) := entry.attr(counter1).attrval(counter2);
end loop;
dbms_ldap.populate_mod_array(user_array,DBMS_LDAP.MOD_ADD,
entry.attr(counter1).attrname,user_vals);
user_vals.delete;
end loop;
-- Add the entry
retval := dbms_ldap.add_s(user_session,user_dn,user_array);
plg_debug('THE RETURN VALUE IS'||retval);
if retval = 0 then
rc := 0;
errormsg:='No error occurred while adding the entry';
else
rc := retval;
errormsg :='Error code'||rc||' while adding the entry';
end if;
-- Free the mod_array
dbms_ldap.free_mod_array(user_array);
retval := dbms_ldap.unbind_s(user_session);
plg_debug('FINISHED ADDING THE ENTRY');
exception
WHEN others THEN
plg_debug (SQLERRM);
end;
end adduser;
/
show error
exit;
The ldapcompare
plug-in can use three new overloaded module interfaces to compare binary attributes. If you want to use these interfaces to develop a plug-in package that handles both binary and nonbinary attributes, you must include two separate procedures in the package. The package name for both procedures is the same because only one orclPluginName
can be registered in the plug-in entry.
After updating an existing plug-in package to include a procedure that compares binary attributes, reinstall the package. Recompile packages that depend on the plug-in package.
The three new interfaces look like this:
PROCEDURE pre_compare (ldapplugincontext IN ODS.plugincontext, dn IN VARCHAR2, attrname IN VARCHAR2, attrval IN BLOB, rc OUT INTEGER, errormsg OUT VARCHAR2 ); PROCEDURE when_compare_replace (ldapplugincontext IN ODS.plugincontext, result OUT INTEGER, dn IN VARCHAR2, attrname IN VARCHAR2, attrval IN BLOB, rc OUT INTEGER, errormsg OUT VARCHAR2 ); PROCEDURE post_compare (ldapplugincontext IN ODS.plugincontext, result IN INTEGER, dn IN VARCHAR2, attrname IN VARCHAR2, attrval IN BLOB, rc OUT INTEGER, errormsg OUT VARCHAR2 );
The example that follows compares a binary attribute of an entry in the plug-in directory with a binary attribute of an entry in another directory. This package replaces the compare code of the server with the compare code of the plug-in. The package handles both binary and nonbinary attributes. As such it contains two separate procedures.
create or replace package compareattr as
procedure when_compare_replace(ldapplugincontext IN ODS.plugincontext,
result OUT integer,
dn IN varchar2,
attrname IN VARCHAR2,
attrval IN BLOB,
rc OUT integer,
errormsg OUT varchar2);
procedure when_compare_replace(ldapplugincontext IN ODS.plugincontext,
result OUT integer,
dn IN varchar2,
attrname IN VARCHAR2,
attrval IN varchar2,
rc OUT integer,
errormsg OUT varchar2);
end compareattr;
/
show error
CREATE OR REPLACE PACKAGE BODY compareattr AS
procedure when_compare_replace(ldapplugincontext IN ODS.plugincontext,
result OUT integer,
dn IN varchar2,
attrname IN VARCHAR2,
attrval IN varchar2,
rc OUT integer,
errormsg OUT varchar2)
is
pos INTEGER := 2147483647;
begin
plg_debug('START');
plg_debug('THE ATTRNAME IS'||attrname||' AND THE VALUE IS'||attrval);
plg_debug('END');
rc := 0;
errormsg :='No error!!!';
exception
WHEN others THEN
plg_debug ('Unknown UTL_FILE Error');
end;
procedure when_compare_replace(ldapplugincontext IN ODS.plugincontext,
result OUT integer,
dn IN varchar2,
attrname IN VARCHAR2,
attrval IN BLOB,
rc OUT integer,
errormsg OUT varchar2)
is
counter pls_integer;
retval pls_integer := -1;
cmp_result integer;
s integer;
user_session DBMS_LDAP.session;
user_entry DBMS_LDAP.message;
user_message DBMS_LDAP.message;
user_dn varchar(256);
user_attrs DBMS_LDAP.string_collection;
user_attr_name VARCHAR2(256);
user_ber_elmt DBMS_LDAP.ber_element;
user_vals DBMS_LDAP.blob_collection;
ldap_host varchar(256);
ldap_port varchar(256);
ldap_user varchar(256);
ldap_passwd varchar(256);
ldap_base varchar(256);
begin
ldap_host :='backup.us.example.com';
ldap_port :='4000';
ldap_user :='cn=orcladmin';
ldap_passwd :='password';
ldap_base := dn;
plg_debug('STARTING COMPARISON IN WHEN REPLACE PLUG-IN');
s := dbms_lob.getlength(attrval);
plg_debug('THE NUMBER OF BYTES OF ATTRVAL'||s);
-- Get a session
user_session := dbms_ldap.init(ldap_host, ldap_port);
-- Bind to the directory
retval := dbms_ldap.simple_bind_s(user_session, ldap_user, ldap_passwd);
-- issue the search
user_attrs(1) := attrname;
retval := DBMS_LDAP.search_s(user_session, ldap_base,
DBMS_LDAP.SCOPE_BASE,
'objectclass=*',
user_attrs,
0,
user_message);
-- Get the entry in the other OID server
user_entry := DBMS_LDAP.first_entry(user_session, user_message);
-- Log the DN and the Attribute name
user_dn := DBMS_LDAP.get_dn(user_session, user_entry);
plg_debug('THE DN IS'||user_dn);
user_attr_name := DBMS_LDAP.first_attribute(user_session,user_entry,
user_ber_elmt);
-- Get the values of the attribute
user_vals := DBMS_LDAP.get_values_blob(user_session, user_entry,
user_attr_name);
-- Start the binary comparison between the ATTRVAL and the attribute
-- values
if user_vals.count > 0 then
for counter in user_vals.first..user_vals.last loop
cmp_result := dbms_lob.compare(user_vals(counter),attrval,
dbms_lob.getlength(user_vals(counter)),1,1);
if cmp_result = 0 then
rc := 0;
-- Return LDAP_COMPARE_TRUE
result := 6;
plg_debug('THE LENGTH OF THE ATTR.'||user_attr_name||' IN THE
ENTRY IS'||dbms_lob.getlength(user_vals(counter)));
errormsg :='NO ERROR. THE COMPARISON HAS SUCCEEDED.';
plg_debug(errormsg);
plg_debug('FINISHED COMPARISON');
return;
end if;
end loop;
end if;
rc := 1;
-- Return LDAP_COMPARE_FALSE
result := 5;
errormsg :='ERROR. THE COMPARISON HAS FAILED.';
plg_debug('THE LENGTH OF THE ATTR.'||user_attr_name||' IN THE ENTRY IS'
||dbms_lob.getlength(user_vals(user_vals.last)));
plg_debug(errormsg);
plg_debug('FINISHED COMPARISON');
-- Free user_vals
dbms_ldap.value_free_blob(user_vals);
exception
WHEN others THEN
plg_debug (SQLERRM);
end;
end compareattr;
/
show error
exit;
This section defines the object types introduced in the Plug-in LDAP API. All of these definitions are in Oracle Directory Server database schema. Note that the API includes object types that enable plug-ins to extract binary data from the database.
create or replace type strCollection as TABLE of VARCHAR2(512); / create or replace type pluginContext as TABLE of VARCHAR2(512); / create or replace type attrvalType as TABLE OF VARCHAR2(4000); / create or replace type attrobj as object ( attrname varchar2(2000), attrval attrvalType ); / create or replace type attrlist as table of attrobj; / create or replace type binattrvalType as TABLE OF BLOB; / create or replace type binattrobj as object ( binattrname varchar2(2000), binattrval binattrvalType ); / create or replace type binattrlist as table of binattrobj; / create or replace type entryobj as object ( entryname varchar2(2000), attr attrlist, binattr binattrlist ); / create or replace type entrylist as table of entryobj; / create or replace type bvalobj as object ( length integer, val varchar2(4000) ); / create or replace type bvallist as table of bvalobj; / create or replace type binvalobj as object ( length integer, binval blob ); / create or replace type binvallist as table of binvalobj; / create or replace type modobj as object ( operation integer, type varchar2(256), vals bvallist, binvals binvallist ); / create or replace type modlist as table of modobj;
When you use the plug-ins, you must adhere to the signature defined for each of them. Each signature is provided here.
PROCEDURE pre_add (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2, entry IN ODS.entryobj, rc OUT INTEGER, errormsg OUT VARCHAR2);
PROCEDURE when_add (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2, entry IN ODS.entryobj, rc OUT INTEGER, errormsg OUT VARCHAR2);
PROCEDURE when_add_replace (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2, entry IN ODS.entryobj, rc OUT INTEGER, errormsg OUT VARCHAR2);
PROCEDURE post_add (ldapplugincontext IN ODS.plugincontext,
result IN INTEGER, dn IN VARCHAR2, entry IN ODS.entryobj, rc OUT INTEGER, errormsg OUT VARCHAR2);
PROCEDURE pre_modify (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2, mods IN ODS.modlist, rc OUT INTEGER, errormsg OUT VARCHAR2);
PROCEDURE when_modify (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2, mods IN ODS.modlist, rc OUT INTEGER, errormsg OUT VARCHAR2);
PROCEDURE when_modify_replace (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2, mods IN ODS.modlist, rc OUT INTEGER, errormsg OUT VARCHAR2);
PROCEDURE post_modify (ldapplugincontext IN ODS.plugincontext,
result IN INTEGER, dn IN VARCHAR2, mods IN ODS.modlist, rc OUT INTEGER, errormsg OUT VARCHAR2);
PROCEDURE pre_compare (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2, attrname IN VARCHAR2, attrval IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 );
PROCEDURE pre_compare (ldapplugincontext IN ODS.plugincontext, dn IN VARCHAR2, attrname IN VARCHAR2, attrval IN BLOB, rc OUT INTEGER, errormsg OUT VARCHAR2 ); PROCEDURE when_compare_replace (ldapplugincontext IN ODS.plugincontext,
result OUT INTEGER, dn IN VARCHAR2, attrname IN VARCHAR2, attrval IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 ); PROCEDURE when_compare_replace (ldapplugincontext IN ODS.plugincontext, result OUT INTEGER, dn IN VARCHAR2, attrname IN VARCHAR2, attrval IN BLOB, rc OUT INTEGER, errormsg OUT VARCHAR2 );
PROCEDURE post_compare (ldapplugincontext IN ODS.plugincontext,
result IN INTEGER, dn IN VARCHAR2, attrname IN VARCHAR2, attrval IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 ); PROCEDURE post_compare (ldapplugincontext IN ODS.plugincontext, result IN INTEGER, dn IN VARCHAR2, attrname IN VARCHAR2, attrval IN BLOB, rc OUT INTEGER, errormsg OUT VARCHAR2 );
PROCEDURE pre_delete (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 );
PROCEDURE when_delete (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 );
PROCEDURE when_delete_replace (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 );
PROCEDURE post_delete (ldapplugincontext IN ODS.plugincontext,
result IN INTEGER, dn IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 );
PROCEDURE pre_search (ldapplugincontext IN ODS.plugincontext,
baseDN IN VARCHAR2, scope IN INTEGER, filterStr IN VARCHAR2, requiredAttr IN ODS.strCollection, rc OUT INTEGER, errormsg OUT VARCHAR2 );
PROCEDURE post_search (ldapplugincontext IN ODS.plugincontext,
result IN INTEGER, baseDN IN VARCHAR2, scope IN INTEGER, filterStr IN VARCHAR2, requiredAttr IN ODS.strCollection, rc OUT INTEGER, errormsg OUT VARCHAR2 );
PROCEDURE pre_bind (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2, passwd IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 );
PROCEDURE when_bind_replace (ldapplugincontext IN ODS.plugincontext,
result OUT INTEGER, dn IN VARCHAR2, passwd IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 );
PROCEDURE post_bind (ldapplugincontext IN ODS.plugincontext,
result IN INTEGER, dn IN VARCHAR2, passwd IN VARCHAR2, rc OUT INTEGER, errormsg OUT VARCHAR2 );