Skip Headers
Oracle® Virtual Directory Product Manual
Release 10g (10.1.4.3.0)
E12283-01
  Go To Table Of Contents
Contents

Previous
Previous
 
Next
Next
 

10 Mapping System

This chapter describes the Oracle Virtual Directory mapping system based on the Python scripting language and contains the following sections:

Introduction

Oracle Virtual Directory includes a bidirectional mapping system based on the Python scripting language. A Mapper is a special Python script, file type .mpy, that processes inbound and outbound transactional data flow within Oracle Virtual Directory. A mapping script can adjust requests as they enter the system on the way to data sources, and transform responses on the return path to the client. When compiled and deployed to the server a mapping script is known as a Mapper.


Note:

If you rename attributes using Mappers, Oracle Virtual Directory supports search on the renamed attribute/value only if the custom code overrides the incoming filter object, as is in the DB_Groups Mapping.

Mappers are configured to run within the Oracle Virtual Directory Plug-in system in what is known as a plug-in chain. As with Plug-ins, a Mapper may be run globally or at an adapter level. Figure 10-1 shows a typical scenario where one mapping may be run on multiple adapters, while another may run only on a specific adapter.

Figure 10-1 Example Mapping Deployment on a Single Adapter and Multiple Adapters

Mappers on one and several adapters

Each mapping has an inbound and outbound flow, which means that a mapping can translate things one way as a request is received and reverse that translation as results are returned to the requesting application. Such programmatic reversal is important, as it is not usually possible for the server to guess the intent.

When to Use a Mapping and When to Use a Custom Plug-in

Most customers who use mappings use default mappings shipped with Oracle Virtual Directory to meet an application requirement. For customization needs, the Java plug-in API is typically used because the Mapper API only handles a subset of Java plug-in functionality and more developers know Java than Python, thus reducing time needed to develop the code.

Mapping Templates

When you create mappings, you can use a predefined mapping template included in Oracle Virtual Directory to simplify the mapping configuration. Oracle Virtual Directory provides several standard "boiler-plate" template mapping scripts. The mapping script names preceded by python are legacy scripts and are seen raw by Oracle Virtual Directory. The mapping script names that are not preceded by python are configurable using Oracle Virtual Directory Manager. To edit the code in the mapping script using Oracle Virtual Directory Manager, open the script using the Resource View (View->Resource) in Oracle Virtual Directory Manager.

The mapping templates with names preceded by OAM or EUS are for use with Oracle Access Manager and Enterprise User Security respectively. The documentation on the integrations with those technologies detail their mappings templates and their usage.

The following is a list and description of the Oracle Virtual Directory mapping templates:

Active_Directory_to_inetOrg

Maps Microsoft Active Directory user and group objects to the InetOrgPerson inetOrgPerson and groupOfUniquenames objects (respectively).

Common_Name_to_Given_Name

Creates a virtual common name attribute by combining values from two attributes, default sn and givenname. The Common_Name_to_Given_Name mapping is typically used with the Database Adapter, which may have only a first and last name, but no full name.

ConditionalPublish

Returns only the specified attribute if the conditional value in another attribute is met. The ConditionalPublish mapping is useful to hide FERPA protected attributes in a higher education environment.

DB_Groups

Allows a database to be used to manage LDAP groups, in particular, the case where the membership value is only the RDN value instead of the complete DN. The DBGroups mapping expands the RDN value into a complete valid virtual DN.

Map_DB_Password

Maps inbound binary syntax passwords to IA5String passwords compatible with the database.

Python Resources

Oracle mappers make extensive use of the Python language with additional Oracle provided functions for LDAP data manipulation. Refer to the following websites for more information on Python:

Example Scenarios

The information in this section provides two examples for mappers:

Example: Constructing A CN Attribute

This example explains how to create a common name (cn) from a givenname and a surname, which could occur when using a database adapter to provide an LDAP interface to a user data stored in a database. While LDAP directories generally store a cn, databases tend to store only a first name and last name. When performing a search, this could become very complicated when filtering on common name. For example, the filter (cn=Marc Boorshtein) would need to read (&(givenName=Marc)(sn=Boorshtein)).

To simplify the process, mapping provides tools for manipulating filters as follows:

From string import split

def parceCN(val):
   return split(val,' ',2)

def inbound():
   #map the "cn" filters
   if operation == 'get':
       if haveAttribute('cn'):
          addAttribute('givenName')
          addAttribute('sn')

   cnFilters = findFilters('cn')  
   for filter in cnFilters:
   target,op,val = filter.contents
   givenNameVal, snVal = parceCN(val)
   givenNameFilter = createFilter('givenName',op,givenNameVal)
   snFilter = createFilter('sn',op,snVal)
   filter.contents =

createAndFilter([givenNameFilter,snFilter]) '

def outbound():
      #outbound stuff
       addAttributeValue('cn',getAttributeValue('givenName') + ' ' +
getAttributeValue('sn'))Œ

The requirements for this mapper are fairly simple. When data is retrieved from the adapter, we would like to form a cn by combining givenname with sn. However on the inbound side, we want to split cn into givenname and sn. If cn is present in the attribute request list, the list will be changed to include givenname and sn. Finally, if the inbound operation is a search operation, we need to check the search filter and convert cn appropriately.

Outbound Processing

The outbound function handles all transactions that are flowing from the adapter to the client. In this example, we want to form a cn from two other values and we use the addAttributeValue function to create a new cn value by combining givenname, a space, and the sn value. Notice how existing values are retrieved using getAttributeValue function. This function retrieves a specific attribute from the current entry being returned to the client.

Inbound Processing

In the inbound() function we want to convert any CN into separate givenname and sn attributes. For a search, we want to convert search filters for cn into combined filter for givenname and sn. We will create a new function, parceCN(), to perform this task.

On the first line, the split function is imported from the Python string module. A Python function called parseCN() is defined that will take a common name (cn) and split it into a first and last name based on detecting a space.


Note:

In reality, this is more complex, for example, what about middle names, but for the purposes of this example, we will cover this simple case.

Next, we define our inbound() function. The inbound function could deal with any LDAP operation, but in this case, we are interested in looking at search operations. The first line after inbound is therefore an if block that tests the value of operation. The variable operation contains one of add, bind, delete, get, modify, or rename.

If operation = get, the mapper proceeds by determining if the search request had cn in the attribute request list(). Since cn can only be formed by combining givenname and sn, we need to add givenname and sn to the search list using the addAttribute function.

In order to handle filter requests for cn, the mapper fetches all filter elements whose target is the cn attribute(). For each filter, the mapper parses it, calculates the corresponding givenname and sn values by calling parseCN, and creates new givenName and sn filters. Finally, it replaces the filter term with cn with a combined filter including the givenName and sn(').

Example: Mapping Active Directory Schema

It is common for an application to require the use of an LDAP directory using inetorgperson and groupofuniquenames schema objects. However, many organizations use Microsoft Active Directory which supports only user and group objects. To bridge this interoperability gap, a mapping can make an Active Directory schema look like inetorg style schema using inetorgperson or groupofuniquenames.

The translation between these two systems has multiple requirements, including:

  • Bidirectional mapping of attributes names. For example uniquemember = member, uid = samaccountname, and so on.

  • Conversion of objectclass names. Not only do the basic objectclass names need to change, but we must also consider that Microsoft Active Directory does not use auxiliary objectclasses. For example, objectclass values of interorgperson, organizationalperson, or person must be collapsed to just user.

  • Adding special attribute values. Microsoft requires the use of additional object type codes such as groupType or userAccountControl. Depending on the operation, special tags must be added to the request.

  • RDN conversion. Microsoft typically uses cn as the relative distinguished name of user accounts. Many applications expect the use of uid.

def inbound():
      #first rename the attributes
      rename({'uniqueMember':'member','uid':'samaccountname','userpassword':
      'unicodepwd','ntgrouptype':'grouptype'})Π

      #map nessasary object class values
      if haveAttributeValue('objectclass','groupifuniquenames'):
      removeAttributeValue('objectclass','groupofuniquenames')
      addAttributeValue('objectclass','group') 

      if haveAttributeValue('objectclass','organizationalPerson'):
      removeAttributeValue('objectclass','organizationalPerson')
      addAttributeValue('objectclass','user')

      if haveAttributeValue('objectclass','inetOrgPerson'):
      removeAttributeValue('objectclass','inetOrgPerson')
      addAttributeValue('objectclass','user')

      #when adding an entry, certain values need to be added
      if operation == 'add':
         if haveAttributeValue('objectClass','group'):
         addAttributeValue('groupType','-2147483646')
           if not haveAttribute('samaccountname'):
                   copy('cn','samaccountname')

         if haveAttributeValue('objectClass','user'):
            addAttributeValue('userAccountControl','66048')

      #collapse aux classes
      removeAttributeValue('objectClass','person')
      removeAttributeValue('objectClass','organizationalPerson')

      #set the rdn
      setRDN('samaccountname','cn')

def outbound():
       #first rename the attributes

       rename({'member':'uniqueMember','samaccountname':'uid','unicodepwd':
       'userpassword','grouptype':'ntgrouptype'})  
     #map nessasary object class values
     if haveAttributeValue('objectclass','group'):
     removeAttributeValue('objectclass','group')
     addAttributeValue('objectclass','groupofuniquenames') 

 if haveAttributeValue('objectclass','user'):
    removeAttributeValue('objectclass','user')
    addAttributeValue('objectclass','organizationalPerson')

Looking at the mapping above, there are two functions defined: inbound() and outbound(). The first line of inbound renames all inetorg attributes to Active Directory attributes. The rename function is called for all operations. For example, if the operation is a search, then all requested attributes will be renamed as well as all attributes in the filter. If the operation is an add or modify, then all attributes effected are renamed.

The next block replaces inetOrg object classes with InetAD ones. Notice that we are able to use conditional statements to determine what actions should be performed.

The third block checks to see if the operation is an add, and if so, it adds the specific attribute information required by Active Directory.

Next, all auxiliary object classes are removed because Active Directory does not allow for an auxiliary object class to be directly specified during an add.

Finally, the RDN is changed from uid to cn. Notice that the code converts samaccountname to cn because uid was already renamed to samaccountname. This does more than just change the rdn from a uid to cn, but it will deal with locating the cn if it's not specified (for example, in a modify or a search).

The outbound() function executes after a request is returned from Active Directory. This function reverses the inbound function by first renaming all applicable attributes, mapping the object class names and changing the rdn of any results. With a small script, an inetOrg application may use an Active Directory.

Mappings provide a simple and flexible way to manipulate requests and responses. You can transform and mold data into whatever form your application requires using mappings.

Mapping Functions

Mappers are based on Python and can use any functions or subroutines available within the Python language. In addition to Python, the following library functions are provided:


Note:

For methods specifying "Map xxxxx", this means you can specify a list of values in the form:
{'uniqueMember':'member','uid':'samaccountname',[…] }

This is essentially an array of one or more mapped values. Use this construct for those methods that support it when a particular method is to be used multiple times for different named pair relationships (for example, rename in the preceding example script). This syntax not only is good shorthand, but also yields improved performance.


Methods

The following information describes the additional methods provided:

appendAttribute(source,destination)

operations: add, modify, get, entry

The appendAttribute function adds the values of the source attribute to the destination attribute. The source attribute remains in place. This function will effect a search filter.

appendAttribute('sn','givenName')
add/entry:dn: cn=User
objectClass: person
cn: User
givenName: User
sn: name
becomes:
dn: cn=User
objectClass: person
cn: User
givenName: User
givenName: name
sn: name

modify:dn: cn=User
changetype: modify
add: sn
sn: Last
-
add: givenName
givenName: First
becomes:
dn: cn=User
changetype: modify
add: sn
sn: Last
-
add: givenName
givenName: First
givenName: Last

get:(&(givenName=first)(sn=last))
becomes:
(&(|(sn=last)(givenName=last))(givenName=first))
copyAttribute(source,destination)

operations: add, modify, get, entry

The copyAttribute function copies attribute values from the source attribute to the destination attribute, overwriting the destination attribute if it already exists.

copyAttribute('sn','givenName')
add/entry: dn: cn=User
objectClass: person
cn: User
givenName: User
sn: name
becomes:
dn: cn=User
objectClass: person
cn: User
givenName: User
givenName: name
sn: name

modify:dn: cn=User
changetype: modify
add: sn
sn: Last
-
add: givenName
givenName: First
becomes:
dn: cn=User
changetype: modify
add: sn
sn: Last
-
add: givenName
givenName: First
givenName: Last

get:(&(givenName=first)(sn=last))
becomes:
(|(sn=last)(givenName=last))
renameAttribute(source,destination)

operations: add, modify, get, entry

Renames the source attribute to the destination attribute. If the destination attribute already exists, it is overwritten. If the source attribute does not exist, but the destination attribute does, the destination attribute is removed.

renameAttribute('sn','givenName')
add/entry:dn: cn=User
objectClass: person
cn: User
givenName: User
sn: name
becomes:
dn: cn=User
objectClass: person
cn: User
givenName: name

modify: dn: cn=User
changetype: modify
add: sn
sn: Last
-
add: givenName
givenName: First
becomes:
dn: cn=User
changetype: modify
add: givenName
givenName: Last

get:(&(givenName=first)(sn=last))
becomes:
 (givenName=last)
removeAttribute(attribute)

operations: add, modify, get, entry

Removes the named attribute, returning its values in a list. If the attribute is a part of an entry, the values are returned. If the value is part of a changelist, the EntryChange object is returned.

removeAttribute('sn')
add/entry:dn: cn=User
objectClass: person
cn: User
givenName: User
sn: name
becomes:
dn: cn=User
objectClass: person
cn: User
givenName: User

modify:dn: cn=User
changetype: modify
add: sn
sn: Last
-
add: givenName
givenName: First
becomes:
dn: cn=User
changetype: modify
add: givenName
givenName: First
givenName: Last

get:(&(givenName=first)(sn=last))
becomes:
(givenName=last)
revalueAttribute(attribute,currentValue,newValue)

operations: add, modify, entry, get

revalueAttribute('sn','name','newname')
add/entry:dn: cn=User
objectClass: person
cn: User
givenName: User
sn: name
becomes:
dn: cn=User
objectClass: person
cn: User
givenName: User
sn: newname

modify:dn: cn=User
changetype: modify
add: sn
sn: name
-
add: givenName
givenName: First
becomes:
dn: cn=User
changetype: modify
add: sn
sn: newname
-
add: givenName
givenName: First
givenName: Last

get:(&(givenName=first)(sn=name))
becomes:
(&(givenName=last)(sn=newname))
mapSyntax(value,newSyntax)
mapSyntax(attribute,newSyntax)

operations: add, modify, entry

Maps a syntax value to a new syntax, or an attribute to a new syntax. If the first argument is a Syntax object, the function will return an instance of Syntax as named by newSyntax. If the first argument is the name of an attribute, all instances of that attribute are mapped to the new syntax. Valid syntaxes are: DirectoryString,IA5String,BinarySyntax,BinarySyntax.

splitValue(newNames,currentName,parseFunction,index,remove)
splitValue(newNames,currentName,parseFunction,index)
splitValue(newNames,currentName,parseFunction,remove)
splitValue(newNames,currentName,parseFunction)
splitValue(newNames,currentName,index,remove)
splitValue(newNames,currentName,index)
splitValue(newNames,currentName,remove)
splitValue(newNames,currentName)

operations: add, modify, entry

splitValue(['givenName','sn','cn',1)
add/entry:dn: uid=User
objectClass: person
cn: First Last
uid: User
becomes:
dn: uid=User
objectClass: person
givenName: First
sn: Last
uid: User

modify:dn: uid=User
changeType: modify
replace: cn
cn: First1 Last1
becomes:
dn: uid=User
changeType: modify
replace: givenName
givenName: First1
-
replace: sn
sn: :Last1

get:(cn=First Last)
becomes:
(&(givenName=First)(sn=last))
addAttributeValue(name,value)

operations: add, modify, entry

Adds a value to the names attribute, or creates it if it does not exist. In the case of modify, if the attribute does not exist, then an Add modification item is created.

addAttributeValue('myattrib','myval')
addAttributeValue('noattrib','hasvalue')
add/entry:dn: uid=User
objectClass: person
cn: First Last
uid: User
myattrib: noval
becomes:
dn: uid=User
objectClass: person
givenName: First
sn: Last
uid: User
myattrib: noval
myattrib: myval
noattrib: hasValue

modify: dn: uid=User
changeType: modify
delete: myattrib
myattrib: someval
becomes:
dn: uid=User
changeType: modify
delete: myattrib
myattrib: someval
myattrib: myval
-
changetype: add
add: noattrib
noattrib: hasValue
haveAttribute(attributeName)
haveAttribute(attributeName,fetchFromServer)

operations: add, modify, entry, get

Returns 1 or 0 (true or false) if the named attribute exists. If fetchFromServer is 1, then the entry is fetched from the server.

haveAttributeValue(attributeName,attributeValue)
haveAttribute(attributeName, ,attributeValue,fetchFromServer)

operations: add, modify, entry, get

Returns 1 or 0 (true or false) if the named attribute and associated value exists. If fetchFromServer is 1, then the entry is fetched from the server for comparison.

removeAttributeValue(attributeName,attributeValue)

operations: add, modify, get, entry

Removes an attribute value, returning true if the value was removed.

removeAttributeValue('myattribute','myvalue')
add/entry:dn: cn=user
objectClass: person
cn: user
myattribute: myvalue
myattribute: myvalue2
becomes:
dn: cn=user
objectClass: person
cn: user
myattribute: myvalue2

modify:dn: cn=User
changetype: modify
replace: myattribute
myattribute: myvalue
-
add: sn
sn: last
becomes:
dn: cn=User
changetype: modify
add: sn
sn: last

get: (&(sn=last)(myattribute=myvalue))
becomes:
(sn=last)
setRDN(oldRDNAttribute,newRDNAttribute)

operations: add, modify, delete, bind, rename, entry

Changes the RDN of the current name (base for get) from the old RDN to a new RDN attribute.

setRDN('cn','uid')
add/entry:        dn: cn=user
                  objectClass: inetOrgPerson
                  uid: userid
                  cn: user
                  becomes:
                  dn: uid=userid
                  cn: user
                  objectClass: inetOrgPerson

modify:           dn: cn=user
                  changetype: modify
                  add: sn
                  sn: last
                  becomes:
                  dn: uid=userid
                  changetype: modify
                  add: sn
                  sn: last

bind/get/delete:  dn: cn=user
                  becomes:
                  dn: uid=userid
addReturnAttribute(attributeName)

operations: get

Adds an attribute to the return attribute list during a search.

findFilters(attributeName)

operations: get

Returns a list of all filters that involve the specified attribute.

createfilter(target,operation,value)

operations: get

Creates a new filter object, with the target being the attribute being tested, the operation being one of the possible comparators and value being the value to filter on.

createAndFilter(filters)

operations: get

Creates an and filter from a list of filters

createOrFilter(filters)

operations: get

Creates an or filter from a list of filters

getAttributeValue(attributeName)

operations: add, entry

Returns the first value of the named attribute

getAttributeValues(entry,attributeName)

operations: any

Retrieves that values of the named attribute from the supplied entry.

createEntryChange(type,attribute,value)

operations: modify

Creates and returns a new EntryChange object.

addEntryChange(entryChange)

operations: modify

Adds en entry change to the list of entry changes.

getByName(dn)

operations: any

Returns the named entry.

convertBase(attributeName,oldBase,newBase)

operations: add, modify, entry, get

Replaces the oldBase with the newBase for the values of the named attribute.

Data Objects

Data objects are variables that are made available from Oracle Virtual Directory to you in the Python environment. Use these variables to get handles to Oracle Virtual Directory data structures and to interpret various objects and status items.

operation

The current operation. Possible values are: add, modify, delete, rename, get, entry, and bind.

vsi

Retrieve a handle to VSI.

attributes

Attributes requested to be returned. Operations: get

base

The current search base. Operations: get

target, op, val = filter.contents and filter.contents = newfilter

Returns and sets the filter in the form of a tuple (target, operation, value).

boolean filter.isAnd

Returns TRUE if filter is an AND filter.

boolean filter.isOr

Returns TRUE if filter is an OR filter.

boolean filter.isNot

Returns TRUE if filter is a NOT filter.

changeEntries

Set of changes for a modify operation. Operations: modify

creds

The current credentials (DN) of the user. Operations: All

entry

The entry to be added or returned from a search. Operations: get, add

filter

The current search filter. Operations: get

name

The entry to be added, bound, modified or deleted. Available for all operations.

request

Retrieve a Value(s): val = request([String name])

Store a Value(s): request(['myname'])='myvalue'

Returns and sets the current request information object attribute specified. This object is used as a method for passing arbitrary information between different mappers or plug-ins that exist for the duration of a specific transaction. For example, during an inbound operation, you can store information that can be used for processing later during the outbound request.

results

Returns and sets result code if an error occurs. Operations: Add, Delete, Modify

scope

The current search scope in the form of 0, 1, or 2 (0 is base, 1 is onelevel, 2 is subtree). Operations: get

typesOnly

Whether the server is returning only types and not values. Operations: get