Skip Headers
Oracle® Identity Management Application Developer's Guide
10g Release 2 (10.1.2)
B14087-02
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

7 Developing Directory Plug-ins

This chapter explains how to use the plug-in framework for Oracle Internet Directory to extend LDAP operations.

This chapter contains these topics:

7.1 Plug-in Prerequisites

To develop Oracle Internet Directory plug-ins, you should be familiar with the following:

7.2 Plug-in Benefits

To extend the capabilities of the Oracle Internet Directory server, you can write your own server plug-in. A server plug-in is a PL/SQL package, shared object or library, or a dynamic link library on Windows that contains your own functions. Oracle supports only PL/SQL plug-ins.

You can extend LDAP operations in the following ways:

For the last one, you may, for example, implement your own password value checking and place it into the Oracle Internet Directory server.

On startup, the directory server loads your plug-in configuration and library. It calls your plug-in functions while processing various LDAP requests.


See Also:

The chapter about the password policy plug-in in Oracle Internet Directory Administrator's Guide. The chapter contains an example of how to implement your own password value checking and place it into the Oracle Internet Directory server.

7.3 What Is the Plug-in Framework?

The plug-in framework is the environment in which the plug-in user can develop, configure, and apply the plug-ins. Each individual plug-in instance is called a plug-in module.

The plug-in framework includes the following:

Follow these steps to use the server plug-in framework:

  1. Write a user-defined plug-in procedure. This plug-in module must be written in PL/SQL.

  2. Compile the plug-in module against the same database that serves as the Oracle Internet Directory backend database.

  3. Register the plug-in module through the configuration entry interface.

7.4 Operation-Based Plug-ins Supported by the Directory

For operation-based plug-ins, there are pre-operation, post-operation, and when-operation plug-ins.

7.4.1 Pre-Operation Plug-ins

The server calls pre-operation plug-in modules before performing the LDAP operation. The main purpose of this type of plug-in is to validate data before the data can be used in the LDAP operation.

When an exception occurs in the pre-operation plug-in, one of the following occurs:

  • When the return error code indicates warning status, the associated LDAP request proceeds.

  • When the return code indicates failure status, the request does not proceed.

If the associated LDAP request fails later on, the directory does not roll back the committed code in the plug-in modules.

7.4.2 Post-Operation Plug-ins

The Oracle Internet Directory server calls post-operation plug-in modules after performing an LDAP operation. The main purpose of this type of plug-in is to invoke a function after a particular LDAP operation is executed. For example, logging and notification are post-operation plug-in functions.

When an exception occurs in the post-operation plug-in, the associated LDAP operation is not rolled back.

If the associated LDAP request fails, the post plug-in is still executed.

7.4.3 When-Operation Plug-ins

The directory calls when-operation plug-in modules while performing standard LDAP operations. The main purpose of this type of plug-in is to augment existing operations within the same LDAP transaction. If either the LDAP request or the plug-in program fails, all the changes are rolled back.

There are different types of When-operation plug-ins.

  • Add-on

  • Replace

You can, for example, use both add-on and replace plug-ins with the ldapcompare operation. If you use the first type, the directory executes its server compare code and executes the plug-in module defined by the plug-in developer. If you use the second type, the directory does not execute its compare code. Instead it relies on the plug-in module to perform the comparison.

Replace plug-ins are supported only in ldapadd, ldapcompare, ldapdelete, ldapmodify, and ldapbind. Add-on plug-ins are supported in ldapadd, ldapdelete, and ldapmodify.

7.5 Designing, Creating, and Using Plug-ins

This section contains these topics:

7.5.1 Designing Plug-ins

Use the following guidelines when designing plug-ins:

  • Use plug-ins to guarantee that when a specific LDAP operation is performed, related actions are also performed.

  • Use plug-ins only for centralized, global operations that should be invoked for the program body statement, regardless of which user or LDAP application issues the statement.

  • Do not create recursive plug-ins. For example, creating a PRE_LDAP_BIND plug-in that itself issues an ldapbind (through the DBMS_LDAP PL/SQL API) statement, causes the plug-in to execute recursively until it has run out of resources.


    Note:

    Use plug-ins on the LDAP PL/SQL API judiciously. They are executed for every LDAP request every time the event occurs on which the plug-in is created.

7.5.1.1 Types of Plug-in Operations

A plug-in can be associated with ldapbind, ldapadd, ldapmodify, ldapcompare, ldapsearch, and ldapdelete operations.

7.5.1.2 Naming Plug-ins

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.

7.5.2 Creating Plug-ins

Creating a 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 plug-ins only in the ODS database schema. You must compile them in the database that serves as the backend database of Oracle Internet Directory.

7.5.2.1 Package Specifications for Plug-in Module Interfaces

Different plug-ins have different package specifications. As Table 7-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 Plug-in Procedures" for details.

Table 7-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 7-2 names the different plug-in procedures. In addition, it lists and describes the parameters that these procedures use.

Table 7-2 Operation-Based and Attribute-Based Plug-in Procedure Signatures

Invocation Context Procedure Name IN Parameters OUT Parameters

Before ldapbind

PRE_BIND

ldapcontext, Bind DN, Password

return code, error message

With ldapbind but replacing the default server behavior

WHEN_BIND_REPLACE

ldapcontext, bind result, DN, userpassword

bind result, return code, error message

After ldapbind

POST_BIND

ldapcontext, Bind result, Bind DN, Password

return code, error message

Before ldapmodify

PRE_MODIFY

ldapcontext, DN, Mod structure

return code, error message

With ldapmodify

WHEN_MODIFY

ldapcontext, DN, Mod structure

return code, error message

With ldapmodify but replacing the default server behavior

WHEN_MODIFY_REPLACE

ldapcontext, DN, Mod structure

return code, error message

After ldapmodify

POST_MODIFY

ldapcontext, Modify result, DN, Mod structure

return code, error message

Before ldapcompare

PRE_COMPARE

ldapcontext, DN, attribute, value

return code, error message

With ldapcompare but replacing the default server behavior

WHEN_COMPARE_REPLACE

ldapcontext, Compare result, DN, attribute, value

compare result, return code, error message

After ldapcompare

POST_COMPARE

ldapcontext, Compare result, DN, attribute, value

return code, error message

Before ldapadd

PRE_ADD

ldapcontext, DN, Entry

return code, error message

With ldapadd

WHEN_ADD

ldapcontext, DN, Entry

return code, error message

With ldapadd but replacing the default server behavior

WHEN_ADD_REPLACE

ldapcontext, DN, Entry

return code, error message

After ldapadd

POST_ADD

ldapcontext, Add result, DN, Entry

return code, error message

Before ldapdelete

PRE_DELETE

ldapcontext, DN

return code, error message

With ldapdelete

WHEN_DELETE

ldapcontext, DN

return code, error message

With ldapdelete but replacing the default server behavior

WHEN_DELETE

ldapcontext, DN

return code, error message

After ldapdelete

POST_DELETE

ldapcontext, Delete result, DN

return code, error message

Before ldapsearch

PRE_SEARCH

ldapcontext, Base DN, scope, filter

return code, error message

After ldapsearch

POST_SEARCH

Ldap context, Search result, Base DN, scope, filter

return code, error message



See Also:


7.5.3 Compiling Plug-ins

Plug-ins are exactly 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:

  1. Syntax checking: PL/SQL syntax is checked, and a parse tree is generated.

  2. Semantic checking: Type checking and further processing on the parse tree.

  3. 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.

7.5.3.1 Dependencies

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.

7.5.3.2 Recompiling Plug-ins

Use the ALTER PACKAGE statement to manually recompile a plug-in. For example, the following statement recompiles the my_plugin plug-in:

ALTER PACKAGE my_plugin COMPILE PACKAGE;

7.5.4 Registering Plug-ins

To enable the directory server to call a plug-in at the right moment, you must register the plug-in with the directory server. Do this by creating an entry for the plug-in under cn=plugin,cn=subconfigsubentry.

7.5.4.1 The orclPluginConfig Object Class

A plug-in must have orclPluginConfig as one of its object classes. This is a structural object class, and its super class is top. Table 7-3 lists and describes its attributes.

Table 7-3 Plug-in Attribute Names and Values

Attribute Name Attribute Value Mandatory?
cn

Plug-in entry name

Yes

orclPluginAttributeList

A semicolon-separated list of attribute names that controls whether the plug-in takes effect. If the target attribute is included in the list, then the plug-in is invoked. Only for ldapcompare and ldapmodify plug-ins.

No

orclPluginEnable

0 = disable (default)

1 = enable

No

orclPluginEntryProperties

An ldapsearch filter type value must be specified. For example, if we specify orclPluginEntryProperties: (&(objectclass=inetorgperson)(sn=Cezanne)), the plug-in is not invoked if the target entry has objectclass equal to inetorgperson and sn equal to Cezanne.

No

orclPluginIsReplace

0 = disable (default)

1 = enable

For WHEN timing plug-in only

No

orclPluginKind

PL/SQL

No

orclPluginLDAPOperation

One of the following values:

ldapcompare
ldapmodify
ldapbind
ldapadd
ldapdelete
ldapsearch

Yes

orclPluginName

Plug-in package name

Yes

orclPluginRequestGroup

A semicolon-separated group list that controls if the plug-in takes effect. You can use this group to specify who can actually invoke the plug-in.

For example, if you specify orclpluginrequestgroup:cn=security,cn=groups,dc=oracle,dc=com when you register the plug-in, the plug-in will not be invoked unless the ldap request comes from the person who belongs to the group cn=security,cn=groups,dc=oracle,dc=com.

No

orclPluginRequestNegGroup

A semicolon-separated group list that controls if the plug-in takes effect. You can use this group to specify who cannot invoke the plug-in. For example, if you specify orclpluginrequestgroup: cn=security,cn=groups,dc=oracle,dc=com, when you register the plug-in, the plug-in is not invoked if the LDAP request comes from the person who belongs to the group cn=security,cn=groups,dc=oracle, dc=com.

No

orclPluginResultCode 

An integer value to specify the ldap result code. If this value is specified, then plug-in will be invoked only if the LDAP operation is in that result code scenario.

This is only for the post plug-in type.

No

orclPluginShareLibLocation

File location of the dynamic linking library. If this value is not present, then Oracle Internet Directory server assumes the plug-in language is PL/SQL.

No

orclPluginSubscriberDNList

A semicolon separated DN list that controls if the plug-in takes effect. If the target DN of an LDAP operation is included in the list, then the plug-in is invoked.

No

orclPluginTiming

One of the following values:

pre
when
post

No

orclPluginType

One of the following values:

operational
attribute
password_policy
syntax
matchingrule

See Also: "Operation-Based Plug-ins Supported by the Directory"

Yes

orclPluginVersion

Supported plug-in version number

No


7.5.4.2 Adding a Plug-in Configuration Entry by Using Command-Line Tools

Plug-ins must be added to Oracle Internet Directory server so that the server is aware of additional operations that must be performed at the correct time.

When the plug-in successfully compiles against the Oracle Internet Directory backend database, create a new entry and place it under cn=plugin,cn=subconfigsubentry.

In the following examples, an entry is created for an operation-based plug-in called my_plugin1. The LDIF file, my_ldif_file.ldif, is as follows:

7.5.4.3 Example 1

The following is an example LDIF file to create such an object:

cn=when_comp,cn=plugin,cn=subconfigsubentry
objectclass=orclPluginConfig
objectclass=top
orclPluginName=my_plugin1
orclPluginType=operational
orclPluginTiming=when
orclPluginLDAPOperation=ldapcompare
orclPluginEnable=1
orclPluginVersion=1.0.1
orclPluginIsReplace=1
cn=when_comp
orclPluginKind=PLSQL
orclPluginSubscriberDNList=dc=COM,c=us;dc=us,dc=oracle,dc=com;dc=org,dc=us;o=IMC
,c=US
orclPluginAttributeList=userpassword

7.5.4.4 Example 2

cn=post_mod_plugin, cn=plugin,cn=subconfigsubentry
objectclass=orclPluginConfig
objectclass=top
orclPluginName=my_plugin1
orclPluginType=operational
orclPluginTiming=post
orclPluginLDAPOperation=ldapmodify
orclPluginEnable=1
orclPluginVersion=1.0.1
cn=post_mod_plugin
orclPluginKind=PLSQL

Add this file to the directory with the following command:

ldapadd -p 389 -h myhost -D binddn -w password -f my_ldif_file.ldif


Note:

The plug-in configuration entry is not replicated. Replicating it would create an inconsistent state.

7.5.5 Managing Plug-ins

This section explains how to modify and debug plug-ins.

7.5.5.1 Modifying 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.

7.5.5.2 Debugging Plug-ins

You can debug a plug-in using the same facilities available for PL/SQL stored procedures.

7.5.6 Enabling and Disabling Plug-ins

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.

7.5.7 Exception Handling

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.

7.5.7.1 Error Handling

Oracle Internet Directory requires that the return code (rc) and error message (errmsg) be set correctly in the plug-in procedures.

Table 7-4 provides the values that are valid for the return code.

Table 7-4 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 

7.5.7.2 Program Control Handling between Oracle Internet Directory and Plug-ins

Table 7-5 shows where plug-in exceptions occur and how the directory handles them.

Table 7-5 Program Control Handling when a Plug-in Exception Occurs

Plug-in Exception Occurred in Oracle Internet Directory Server Handling

PRE_BIND, PRE_MODIFY, PRE_ADD, PRE_SEARCH, PRE_COMPARE, PRE_DELETE

Depends on return code. If the return code is:

  • Greater than zero (error), then no LDAP operation is performed

  • -1 (warning), then proceed with the LDAP operation

POST_BIND, POST_MODIFY, POST_ADD, POST_SEARCH, WHEN_DELETE

LDAP operation is completed. There is no rollback.

WHEN_MODIFY, WHEN_ADD, WHEN_DELETE

Rollback the LDAP operation


Table 7-6 shows how the directory responds when an LDAP operation fails.

Table 7-6 Program Control Handling when an LDAP Operation Fails

LDAP Operation Fails in Oracle Internet Directory Server Handling

PRE_BIND, PRE_MODIFY, PRE_ADD, PRE_SEARCH, WHEN_DELETE

Pre-operation plug-in is completed. There is no rollback.

POST_BIND, POST_MODIFY, POST_ADD, POST_SEARCH, WHEN_DELETE

Proceed with post-operation plug-in. The LDAP operation result is one of the IN parameters.

WHEN_MODIFY, WHEN_ADD, WHEN_DELETE

When types of plug-in changes are rolled back.

WHEN

Changes made in the plug-in program body are rolled back.


7.5.8 Plug-in LDAP API

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.

7.5.9 Plug-ins and Replication

These cases can cause an inconsistent state in a replication environment:

  • Plug-in metadata replicated to other nodes

  • Changes to directory entries by plug-in programs or other LDAP operations

  • Plug-in installation on only some of the participating nodes

  • Implementation in the plug-in of extra checking that depends on the directory data

7.5.10 Plug-in and Database Tools

Bulk tools do not support server plug-ins.

7.5.11 Security

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

7.5.12 Plug-in Debugging

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/password @$ORACLE/ldap/admin/oidspdsu.pls
    
    
  • To enable plug-in debugging, run this command:

    % sqlplus ods/password @$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/password @$ORACLE/ldap/admin/oidspdof.pls
    
    
  • To display the debug messages that you put in the plug-in module, run this command:

    % sqlplus ods/password @$ORACLE/ldap/admin/oidspdsh.pls
    
    
  • To delete all of the debug messages from the debug table, run this command:

    % sqlplus ods/password @$ORACLE/ldap/admin/oidspdde.pls
    

7.5.13 Plug-in LDAP API Specifications

Here is the package specification that Oracle Internet Directory provides for the 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;

7.5.14 Database Limitations

Oracle Internet Directory10g Release 2 (10.1.2) can use several different versions of the Oracle Database for storing directory data. These include Oracle9i Database Server Release 2, v9.2.0.6 or later and Oracle Database 10g, v10.1.0.4 or later.

In Oracle Application Server 10g Release 2 (10.1.2), the following plug-in features are not supported in the directory server running against Oracle9i Database Server 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.

7.6 Examples of Plug-ins

This section presents two sample plug-ins. One logs all ldapsearch commands. The other synchronizes two directory information trees (DITs).

7.6.1 Example 1: Search Query Logging

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 operational 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:

  1. 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;
    
    
  1. 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;
    /
    
    
  1. 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;
    /
    
    
  1. Register the plug-in entry in Oracle Internet Directory.

    cn=post_search,cn=plugin,cn=subconfigsubentry
    objectclass=orclPluginConfig
    objectclass=top
    orclPluginName=ldap_plugin_example1
    orclPluginType=operational
    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 –w passwd –v \
              –f register_post_search.ldif
    

7.6.2 Example 2: Synchronizing Two DITs

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 post ldapmodify 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: authPassword, creatorsname, createtimestamp, modifiersname, modifytimestamp, pwdchangedtime, pwdfailuretime, pwdaccountlockedtime, pwdexpirationwarned, pwdreset, pwdhistory, pwdgraceusetime.

The following attributes are used the most in the deployment environment and should be excluded from synchronization first: pwdchangedtime, pwdfailuretime, authpassword, pwdaccountlockedtime.


  1. 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.

  2. 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;
    /
    
    
  3. 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;
    /
    
    
  4. Register the plug-in entry with Oracle Internet Directory.

    Construct the LDIF file register_post_delete.ldif:

    cn=post_delete,cn=plugin,cn=subconfigsubentry
    objectclass=orclPluginConfig
    objectclass=top
    orclPluginName=ldap_plugin_example2
    orclPluginType=operational
    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 –w passwd –v –f register_
    post_delete.ldif
    

7.7 Binary Support in the Plug-in Framework

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.

7.7.1 Binary Operations with ldapmodify

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=acme,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.oracle.com';
      ldap_port :='4000';
      ldap_user :='cn=orcladmin';
      ldap_passwd :='welcome';
 
      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=acme,
         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 occured 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;

7.7.2 Binary Operations with ldapadd

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=acme,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.oracle.com';
      ldap_port :='4000';
      ldap_user :='cn=orcladmin';
      ldap_passwd :='welcome';
 
      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=acme,
         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 occured 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;

7.7.3 Binary Operations with ldapcompare

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.oracle.com';
      ldap_port :='4000';
      ldap_user :='cn=orcladmin';
      ldap_passwd :='welcome';
      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;

7.8 Database Object Types Defined

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;

7.9 Specifications for Plug-in Procedures

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
);