Copyright 2015-2017, Tremolo Security, Inc.

MyVD LDAP Virtual Directory

Welcome to the home of MyVD, a Java based LDAP Virtual Directory.

What is a virtual directory?

A virtual directory is a service (typically provied via LDAP) that allows for the integration of multiple data repositories in a transparent manner to an application. There are several use cases for a virtual directory including (but not limmited to):

  • Creating a view of a user with attributes stored in multiple repositories

  • Formatting user and group object for an application

  • Creating a larger directory from smaller ones

To learn how a virtual directory can help integrate your applications with your identity data view some virtual directory use cases.

MyVD Overview

Virtual Directory Use Cases

Virtual Directories have several use cases. In general a virtual directory is most easily used to help integrate an application into an existing environment. Each of the below usecases is examined in more detail with a description of how MyVD might solve the given problem.

  • Namespace Integration - Integrating multiple directories to form a larger single directory

  • Delegated Authentication - Delegating authentication to an enterprise directory while using an edge directory for identity data

  • Data Transformation - Transform existing directory data for use by an application

  • Edge Directory Integration - Integrate application specific directory data into an enterprise directory without making any changes to the enterprise directory.

  • Web Services Integration - Many organizations have web services for the creation and update of user information

Namespace Integration

A typical situation in many enterprise architectures is to maintain a seperate directory for internal and external users. Internal users being employees while the external directory may store contractors and supliers or even customers. In situations where all users need access to an applicaiton (such as a portal) the application may not support multiple directories. In this situation a virtual directory can be used to integrate the directories into a single namespace.

Namespace Integration

In the above example MyVD is used with two LDAP Inserts to create a single view of the two directories.

Delegated Authentication

In certain situations its desirable to have a local directory for user information while an external directory is used for authentication. For instance a local directory for user data and ActiveDirectory for authentication. In order to achieve this there are two options:

  1. Synchronize Passwords from ActiveDirectory

  2. Use a virtual directory to delegate authentication to the ActiveDirectory while delegating all other requests to the local directory

Its not allways acceptable to utilize a password synchronization system for both policy and technical reasons. In this case a virtual directory can be used to delegate the authentication. In the below diagram the virtual directory sits as a proxy between the application and the local directory delegating all bind requests to the corporate ActiveDirectory utilizing Kerberos. This is not the only way to delegate authenticaiton but is one of the simplest.

Delegated Authentication

Data Transformation

While LDAP v3 is an IETF standard, not all applications abide by the standards. In addition to requiring certain attributes, some applications will not work if your directory does not conform to its requirements. Again as above either a virtual directory can be utilized or a new directory can be created based on a synchronization solution. In this example an application requires that users id attribute be called uid while your directory is a Microsoft ActiveDirectory (which stores the user’s id in the samAccountName attribute). In order to support this application MyVD can be configured to "rename" uid to samAccountName.

Data Transformation

The above diagram shows the application performing a search with the filter "(uid=username)" that is transformed to "(samAccountName=username)". When the object is returned the attribute samAccountName is renamed to uid. This is a very basic but powerful example of how a virtual directory can perform data transformation.

Edge Directory Integration

Many organizations have a single enterprise directory that stores common user attributes and may be used for authentication. If an application requires additional attributes it’s generally very dificult to add these attributes to the enterprise directory. In this situation a virtual directory can be used to extend the enterprise directory by creating an edge directory that joins the enterprise directory to application specific attributes.

Edge Directory Use Case

In the above example application attributes are stored in a relational database (as it may be easier to get an application specific database then application specific directory). The Joiner insert combines the database and the enterprise directory based the uid attribute.

Web Services Integration

In order to control the provisioning of users some organizations require users are updated via a common API or web service. A virtual directory can be used to call this web service when users are updated to integrate with the existing infrastructure. The below image depicts the integration of a web service through a custom insert to call the web sevice and a routing insert to redirect updates to the custom insert.

Web Services Integration

Introduction to MyVD

MyVD is a java based virtual directory that utilizes a series of layers and routing to create an identity infrastructure.

Components of MyVD

  • Server - The server is the top level component that controls all of the parts of the virtual directory.

  • Inserts - An insert is a module used to act on a request and optionally generate a response. All functionality is derived from inserts.

  • Chains - Chains are a sequential set of inserts that comprise a single data flow.

  • Namespace - An area of the directory based on a DN. For instance there is a global namespace for all namespaces and there may be additional namespaces such as "ou=people,dc=domain,dc=com". Namepaces are used to route requests and contain chains of inserts.

  • Router - The router combines the global namespace with local namespaces. The router is not directly configurable but can be manipulated via inserts.

How The Pieces Fit

The above pieces of MyVD fit together to create a "flow" data. To illustrate this lets show how the following virtual directory would work:

  • User data is stored in a relational database

  • Authentication is provided via ActiveDirectory

  • Write operations are performed using a custom web service

In order to fulfill the above requirements a virtual directory will need to be built with:

  • A routing insert to redirect all write operations

  • Two namespaces to represent user data

    • Master - ou=people,dc=domain,dc=com with a single insert

      • Custom insert to call the web service needed to update the database

    • DB - ou=people,dc=domain,dc=com with two inserts

      • Kerberos insert for authenticating against ActiveDirectory

      • Database insert to expose the database

We aren’t going to cover the specific configuration in this section, but we will show how these pieces fit together:

MyVD Sample Setup

The above image shows all write operations (add, modify, delete rename) are directed towards the "Master" namespace while all read operations (search, modify) are sent to the DB namespace. This decision is made by the routing insert. Notice that the two namespaces have the same base (ou=people,dc=domain,dc=com). MyVD allows for namespaces to overlap. In this case there is no conflict because the routing plugin determines which namespace is utilized.

Once a namespace is determined, it’s chain is executed. The "Master" namespace has only a single insert which calls a custom web service to update a user’s profile. The "DB" namespace has two inserts on it’s chain. The first insert enables the use of Kerberos during the LDAP "bind" operation. Since this insert handles the bind operation, execution stops there. On searches and compares however the Kerberos insert "ignores" the request by passing it down through the chain to the database insert which is configured to work with the database storing user data.

Next Steps

The above example shows how a complex identity requirement could be implemented using MyVD. From here you can explore the various inserts that are currently provided or how to configure MyVD.

Configuring MyVD

MyVD is configured using a properties file. The properties file defines what port the server will listen on, which inserts will run on the global and local namespaces.

Basic Configuration Concepts

The MyVD configuration file is broken into three parts:

  • Listener

  • Global Namespace

  • Local namespaces

The listener portion defines what port the server will listen on and if there is a secure port. The global namespace defines what inserts will run for all requests and the local namespaces defines what inserts will run for individual namespaces.

Configuring the Listener

The listener can be configured to listen on a secure port, a non secure port or both. To configure a non secure port simply supply a port number. In addition to the port number, you can specify a maximum number of entries or a maximum time limit (in milli-seconds). If not configured, the default is unlimited. The listener can be limited only on a particular host (default is 0.0.0.0). If authentication is required the simple authentication credential are needed to bind the listener (default false).

#Listen on port 389
server.listener.host=localhost
server.listener.port=389
server.listener.authRequired=true
server.listener.maxSizeLimit=1000
server.listener.maxTimeLimit=60000

To open a secure port you need to know the port, have a keystore to provide a key for encryption and the password for that keystore.

#Listen on 636 using SSL
server.secure.listener.port=636
server.secure.keystore=/var/keystores/myvd.ks
server.secure.keypass=secret

Configuring Namespaces

Namespaces are configured by first specifying a list of inserts and then configuring each interceptor. To illustrate how to configure MyVD we will configure the virtual directory described in the MyVD overview. This virtual directory uses a relational database to store users while utilizing ActiveDirectory for authentication and a custom webservice for updating user information. Bellow is a diagram describing the virtual directory.

MyVD Sample Setup

Configuring the Global Namespace

The global namespace is setup by first listing the inserts that are used in the global chain and then each insert is configured.

#First specify the inserts in the global namespace's chain
server.globalChain=insert1,insert2,insert3

#Configure insert1
server.globalChain.insert1.className=com.package.class1
server.globalChain.insert1.config.option1=value1
server.globalChain.insert1.config.option2=value2

#Configure insert2
server.globalChain.insert2.className=com.package.class2
server.globalChain.insert2.config.option1=value1
server.globalChain.insert2.config.option2=value2

#Configure insert3
server.globalChain.insert3.className=com.package.class3
server.globalChain.insert3.config.option1=value1
server.globalChain.insert3.config.option2=value2

In the above diagram there a single "global" insert: the RoutingPlugin. This insert is used to instruct the router how to route certain requests and is described in the "Insert Reference" section. Based on the above diagram the global namespace configuration would be as follows.

#first configure the global chain
server.globalChain=router

#routing plugin to forward all writes to the master and reads to the database
server.globalChain.router.className=net.sourceforge.myvd.inserts.routing.MasterReplicaRouter
server.globalChain.router.config.specifyToInclude=false
server.globalChain.router.config.readOnly=DB
server.globalChain.router.config.master=Master

Configuring the Local Namespaces

MyVD is configured to have multiple "namespaces" which determine the flow of data through the system. Namespaces are separated by LDAP DNs. Like the global namespace, local namespaces contain chains of inserts. Unlike the global namespaces, local namespaces are separated by an LDAP DN and a weight to determine which namespace takes priority when there is a conflict.

To configure local namespaces:

#First, list all the namespaces
server.namespaces=ns1,ns2

#configure the ns1 namespace
server.ns1.chain=insert1,insert2
server.ns1.nameSpace=ou=People,dc=domain,dc=com
server.ns1.weight=100
server.ns1.enabled=true

#Configure insert1
server.ns1.insert1.className=com.package.class1
server.ns1.insert1.config.option1=value1
server.ns1.insert1.config.option2=value2

#Configure insert2
server.ns1.insert2.className=com.package.class2
server.ns1.insert2.config.option1=value1
server.ns1.insert2.config.option2=value2

#configure the ns2 namespace
server.ns2.chain=insert2
server.ns2.nameSpace=ou=Groups,dc=domain,dc=com
server.ns2.weight=100
server.ns2.enabled=true

#Configure insert2
server.ns1.insert2.className=com.package.class2
server.ns1.insert2.config.option1=value1
server.ns1.insert2.config.option2=value2

In the above virtual directory there are two namespaces : Master & DB. The Master namespace has a custom built insert for updating identity data via a webservice. The DB namespace is configured with the kerberos insert and a database insert.

server.namespaces=Master,DB

server.Master.chain=webservice
server.Master.nameSpace=ou=People,dc=domain,dc=com
server.Master.weight=100
server.Master.enabled=true
server.Master.webservice.className=com.mycompany.webservice.Insert
server.Master.webservice.config.url=http://someserver.somehost.com/myservice

server.DB.chain=kerberos,db
server.DB.nameSpace=ou=People,dc=domain,dc=com
server.DB.weight=100
server.DB.kerberos.className=net.sourceforge.myvd.inserts.kerberos.KerberosInterceptor
server.DB.db.className=net.sourceforge.myvd.inserts.jdbc.JdbcInsert
server.DB.db.config.driver=com.db.driver.Driver
server.DB.db.config.url=jdbc:db://server/db
server.DB.db.config.user=user
server.DB.db.config.pass=secret
server.DB.db.config.rdn=uid
server.DB.db.config.mapping=uid=id,cn=name
server.DB.db.config.objectClass=inetOrgPerson
server.DB.db.config.sql=SELECT id,name FROM users

The first line defines the which namespaces will exist. Then each namespace and it’s chain are defined. There are two things to note about this configuration:

  • The custom web service insert is configured in the same fashion as the pre-built inserts. This is because all functionality is derived from inserts.

  • Both namespaces have the same weight, but there are no conflicts. This is because the routing insert in the global plugin explictly sets the namespace to be used.

#List all the namespaces
server.namespaces=ns1,ns2

#Configure the ns1 namespace
server.ns1.chain=insert1
server.ns1.nameSpace=ou=People,dc=mycomp1,dc=com
server.ns1.weight=100
server.ns1.enabled=true

#Configure insert1
server.ns1.insert1.className=com.package.class1
server.ns1.insert1.config.option1=value1
server.ns1.insert1.config.option2=value2

#Configure the ns2 namespace
server.ns2.chain=insert2
server.ns2.nameSpace=ou=Users,dc=mycomp2,dc=org
server.ns2.weight=100
server.ns2.enabled=true

#Configure insert2
server.ns1.insert2.className=com.package.class2
server.ns1.insert2.config.option1=value1
server.ns1.insert2.config.option2=value2

Complete Config

Below is the complete server’s configuration.

#Listen on port 389
server.listener.port=389

#Listen on 636 using SSL
server.secure.listener.port=636
server.secure.keystore=/var/keystores/myvd.ks
server.secure.keypass=secret

#first configure the global chain
server.globalChain=router

#routing plugin to forward all writes to the master and reads to the database
server.globalChain.router.className=net.sourceforge.myvd.inserts.routing.MasterReplicaRouter
server.globalChain.router.config.specifyToInclude=false
server.globalChain.router.config.readOnly=DB
server.globalChain.router.config.master=Master

server.namespaces=Master,DB

server.Master.chain=webservice
server.Master.nameSpace=ou=People,dc=domain,dc=com
server.Master.weight=100
server.Master.enabled=true
server.Master.webservice.className=com.mycompany.webservice.Insert
server.Master.webservice.config.url=http://someserver.somehost.com/myservice

server.DB.chain=kerberos,db
server.DB.nameSpace=ou=People,dc=domain,dc=com
server.DB.weight=100
server.DB.enabled=true
server.DB.kerberos.className=net.sourceforge.myvd.inserts.kerberos.KerberosInterceptor
server.DB.db.className=net.sourceforge.myvd.inserts.jdbc.JdbcInsert
server.DB.db.config.driver=com.db.driver.Driver
server.DB.db.config.url=jdbc:db://server/db
server.DB.db.config.user=user
server.DB.db.config.pass=secret
server.DB.db.config.rdn=uid
server.DB.db.config.mapping=uid=id,cn=name
server.DB.db.config.objectClass=inetOrgPerson
server.DB.db.config.sql=SELECT id,name FROM users

Next Steps

At this point you have been shown how to configure MyVD. From here you can look at the available inserts or how to build your own.

Services and Access Management Inserts

These inserts are primarily concerned with the running of MyVD including access management, server management and server tracking.

The RootObject insert creates a single object meant to be a placeholder. It’s most useful when combining two data sources but want to create a full tree for browsing.

Class Name

net.sourceforge.myvd.inserts.RootObject

Scope

Search

Access Controls

The access controls for MyVD are based on a draft RFC. The RFC can be read [[www.cnn.com] here]. The access control system utilizes a list of access control items. Each item is structured as:

Configuring The Access Control Insert

Class Name

net.sourceforge.myvd.inserts.ldap.LDAPInterceptor

Scope

Search,Compare,Bind,Add,Modify,Delete,Rename

Configuration Options

numACIs

The number of ACIs to be processed

aci.X

An ACI to process based on the below configuration. "X" is the number of the ACI and should begin with 0.

Specifying an ACI
scope-base#scope#grant/deny:permisions#scope#users

The below table explains each part of the ACI:

Component

Description

Example

scope-domain

The base of the scope this ACI applies to. This ACI will only be considered for operations that are subordinates of this DN

ou=people,dc=domain,dc=com

scope

The scope of this ACI. The scope can either be "subtree" to apply to all subordinates of the scope-base or "entry" to apply only to the scope-domain.

subtree OR entry

grant/deny

Determines if the outcome of this ACI should result in a grant or deny

grant OR deny

permisions

Which permisions this ACI grants or denys. See the below table for a list of permisions

scope

Whether this applies to entry results or attributes. The below table explains the different options

[entry] OR [all] OR a comma sepperated list of attribute names

users

Which users this ACI applies to. The below table lists the possible values for this option

See the below table

Entry Permissions

Permission

Letter

Description

Create

c

Determines if entries may be created

Delete

d

Determines if the entry may be deleted

View

v

Determines if the entry may be viewed

Rename

n

Determines if the entry can be renamed

Attribute Permissions

Permission

Letter

Description

Read

r

Determines if the attribute can be read

Write

w

Determines if the attribute can be written to

Obliterate

o

Determines if the attribute can be removed from the entry

Search

s

Determines if the attribute can be included in a search filter

Compare

c

Determines if the attribute can be included in a compare operation

Presence Search

p

Determines if a filter on the presence of the attribute in the entry

Users Options

Users Option

Description

Syntax

Example

Sub Tree

This ACI applies to all users beneath the given DN

subtree:DN

subtree:ou=users,dc=domain,dc=com

DN

This ACI applies to the specified DN only

dn:DN

dn:cn=admin,ou=admins,dc=domain,dc=com

This

The ACI applies to the currently bound user. This is useful for permisions such as letting a user rest their own password

this:

Group

An LDAP static group is used to apply this ACI

group:DN_OF_GROUP

group:cn=admins,ou=groups,dc=domain,dc=com

Dynamic Group

An LDAP dynamic group is used to apply this ACI

dynamic-group:DN_OF_DYNAMIC_GROUP

dynamic-group:cn=admins,ou=groups,dc=domain,dc=com

Public

This ACI applies to all users, bound an anonymous

Access Log

The access log provides information about who is accessing MyVirtualDirectory. The access log provides the ability to roll when the log reaches a certain size or at a certain time.

Class Name

net.sourceforge.myvd.inserts.AccessLog

Scope

All

Configuration Options

fileName

The path and name of the file, ie /var/log/myvd.log

type

rolling or periodic, based on if the log files should roll over based on the size of the log file or the time of the day. If periodic the log will roll at midnight every nights

maxFileSile

When rolling, the size when to roll. Default is 100MB

backupIndex

The maximum number of files to keep

DumpTransaction

This insert dumps the details of a request to the log. DumpTransaction is very useful when debugging a MyVD deployment. By placing this insert at various points in the server it’s possible to see how requests and responses are effected. Note that this insert can have an adverse effect on performance.

Class Name

net.sourceforge.myvd.inserts.DumpTransaction

Scope

All

Configuration Options

logLevel

One of info,debug,error,warn

label

A label to describe this DumpTransaction insert in the logs

RootDSE Insert

This insert should be placed in a namespace with an empty "nameSpace" property in order to construct a root dse object.

Class Name

net.sourceforge.myvd.inserts.RootDSE

Scope

Search

Configuration Options

supportedFeatures

Comma separated list

namingContexts

Pipe " u007C" separated list of DNs

supportedControls

Comma separated list

supportedSaslMechanisms

Comma separated list

supportedExtensions

Comma separated list

Password Change Operation

This insert is an example of how an extended operation could be implemented in MyVD. The insert reconstructs the operation in order to make sure that the request is routed properly.

Class Name

net.sourceforge.myvd.inserts.extensions.PasswordChangeOperation

Scope

Extended Operation

Configuration Options

remoteBase

The final base of the operation

localBase

The base to expect in the operation

Master Replica Router

The Master Replica Router is an insert that should be added to the global chain when you wish to seperate write operations from search operations, such as when utilizing MyVD in a master/replica environment.

Class Name

net.sourceforge.myvd.inserts.routing.MasterReplicaRouter

Scope

Add, Modify, Rename, Delete, Compare, Search, Bind, Extended Operations

Configuration Options

specifyToInclude

true or false, if the servers included by the readOnly option specifies the namespaces to be read only or those NOT to be considered read only.

readOnly

The names of namespaces configured that will be used for search operations

master

The nams of a namespace used to write operations

Schema Insert

This insert allows for a schema ldif file to be used as a schema entry

Class Name

net.sourceforge.myvd.inserts.SchemaInsert

Scope

Search

Configuration Options

schemaLDIF

Path to the ldif file. NOTE: this ldap file MUST contain a full schema entry, including DN and objectClasses. The DN should match the root of this insert. The first line of the ldif file MUST be "version: 1"

Route by Attribute Value

The Route by Attribute Value Router is an insert that should be added to the global chain when requests must be routed to a particular namespace based on an attribute value. An example would be when using MyVD to route requests to different directories based on an email suffix. Additionaly, a default route can be used when no other routes are available. This router is only executed on searches who’s filters are equality ("="). This filter is not applied to substring filters.

Class Name

net.sourceforge.myvd.inserts.routing.RouteByAttributeValue

Scope

Search

Configuration Options

attributeName

The name of the attribute to check

useDefault

true or false, If true then if no route is found a default one is used. If false then if not route is found then MyVD will no route the request to a specific namespace

defaultNameSpace

The namespace from MyVD’s configuration (the name of the namespace, not the DN base) to route to if no routing rules match

ignorePattern

A pattern that identifies requests that should be ignored by this router

ignoreNegative

If set to true, then this insert will ignore anything that DOESN’T match the ignorePattern configuration option

dontRouteBelow

If set, all routeing below the given dn is ignored

numRoutes

The number of routing rules being configured

route.X

Where X is a number between 0 and (numRoutes - 1) inclusively. The value is namepsace=regex. The namespace is the name of the namespace in the MyVD configuration. The regex is a Java regular expression that will be tested against the values for the named attribute in the search’s LDAP filter. Here is the Java pattern language.

This insert should be installed on a namespace that should be allowed to fail when performing searches without causing an error to be returned to the client. When an error does occur, it is logged as a warning.

Class Name

net.sourceforge.myvd.inserts.routing.RequiredForSearch

Scope

Search

Configuration Options

required

true or false, If false, any errors that occur during a search are logged as a warning. If true, this insert is not run.

Mapping Inserts

These inserts are general mapping inserts Certain mapping inserts are specialized for databases or directories. These specialized inserts are documented with their respective categories.

Attribute Cleaner

This insert ensures that the attributes requested by a search request are the only attributes returned to the user. This insert is usefull when additional attributes are added to an entry for meta data purposes but should not be returned to the user.

Class Name

net.sourceforge.myvd.inserts.mapping.AttributeCleaner

Scope

Search

Configuration Options

clearAttributes

If set to true after storing the names of the requested attributes all attributes are requested

Add Static Attribute

This insert allows for the addition of a static attribute value for a particular objectClass. It is useful for adding attributes to identify users based on their directory or namespace.

Class Name

net.sourceforge.myvd.inserts.mapping.AddAttribute

Scope

Search

Configuration Options

attributeName

The name of the attribute to create

attributeValue

The value of the attribute to create

objectClass

The objectClass to create this attribute on

Attribute Mapper

The attribute mapper insert is used to map the names of attributes. For instance it could map uid to samaccountname. This insert works on attribute names in both entries, dns and filters.

Class Name

net.sourceforge.myvd.inserts.mapping.AttributeMapper

Scope

Search,Add,Modify,Delete,Compare,Rename

Configuration Options

mapping

Comma seperated list of attribute maps. For instance "uid=samaccountname"

Attribute Value Mapper

The attribute value mapper is used to map one entry value to another. For instance mapping "objectClass: inetOrgPerson" → "objectClass: user".

Class Name

net.sourceforge.myvd.inserts.mapping.AttributeValueMapper

Scope

Search,Add,Modify,Compare

Configuration Options

mapping

Comma sepperated list of mappings in the form of "attribute.curvalue=newvalue". For instance "objectClass.inetOrgPerson=user

Composite Attribute Insert

The composite attribute insert is designed to create a singe attribute from multiple attributes. Uses include creating a CN attribute from the givenName and sn as well as creating a uid from the same two attributes.

Class Name

net.sourceforge.myvd.inserts.jdbc.CompositeAttrib

Scope

Search

Configuration Options

attribute

The name of the attribute that will be created (ie uid or cn)

components

Comma seperated list of component attributes with a colon to determine how much of the attribute. The length of the compoenent can be a number, fs for the first space, ls for the last space and * for the entire attribute. ie "givenName:1,sn:*"

properCase

true or false. This configurate determines if the first letter of a component should be upper case on inbound transactions and lowercase in outbound transactions.

objectClass

The objectClass that this insert will apply to.

DN Attribute Mapper

The DN attribute map the bases of DN attributes (such as uniqueMember or manager)

Class Name

net.sourceforge.myvd.inserts.mapping.DNAttributeMapper

Scope

Add,Modify,Search

Configuration Options

dnAttribs

The name of all attributes that are DNs

urlAttribs

The name of all the attributes that are LDAP URLs

remoteBase

The remote base to be mapped from

localBase

The local base to be mapped to

SetRDN Insert

This insert allows for the RDN of an entry to be changed. For instance if the current RDN is the "CN" attribute this insert can change it to the "uid" attribute.

Class Name

net.sourceforge.myvd.inserts.setrdn.SetRDN

Scope

Search,Add,Modify,Delete,Rename,Bind

Configuration Options

internalRDN

The current RDN attribute name.

externalRDN

The RDN attribute that will be presented

objectClass

The object class to act on, defaults to inetOrgPerson

dnattributes

List of DN attributes to also map

Dn2Attribute

This insert will map an attribute storing a DN to an attribute value on the referenced object. For instance this insert could map the uniqueMember DN to the uid of the user object pointed to be the DN. This would be useful when mapping from a groupOfUniqueNames to a posixGroups object.

Class Name

net.sourceforge.myvd.inserts.mapping.Dn2Attribute

Scope

Search

Configuration Options

newValueAttribute

The attribute that will contain the attribute values

sourceAttribute

The attribute to pull the DNs from

FormatDate

This insert will format directory value stored as the number of milliseconds since epoch.

Class Name

com.tremolosecurity.proxy.myvd.inserts.format.FormatDate

Scope

Search, Post Search Entry

Configuration Options

attributeName

The name of the attribute to create

format

Format to use (see below)

sourceAttributeName

The name of the attribute to get the date from

Table 1. Date Time Formatting

Symbol

Meaning

Presentation

Examples

G

era

text

AD

C

century of era (>=0)

number

20

Y

year of era (>=0)

year

1996

x

weekyear

year

1996

w

week of weekyear

number

27

e

day of week

number

2

E

day of week

text

Tuesday; Tue

y

year

year

1996

D

day of year

number

189

M

month of year

month

July; Jul; 07

d

day of month

number

10

a

halfday of day

text

PM

K

hour of halfday (0~11)

number

0

h

clockhour of halfday (1~12)

number

12

H

hour of day (0~23)

number

0

k

clockhour of day (1~24)

number

24

m

minute of hour

number

30

s

second of minute

number

55

S

fraction of second

millis

978

z

time zone

text

Pacific Standard Time; PST

Z

time zone offset/id

zone

-0800; -08:00; America/Los_Angeles

'

escape for text

delimiter

''

single quote

literal

'

Attribute2DN

The Attribute2DN insert is useful for translating an attribute value to a full DN by doing a search. This is useful when using MongoDB for storing groups where a user identifier is stored in a document instead of a full DN. This insert will then translate that attribute into a full DN by searching for the user named in the attribute.

Class Name

com.tremolosecurity.myvd.inserts.mapping.Attribute2DN

Scope

Search, Post Search Entry

Configuration Options

attributeName

The name of the attribute in the document to map to a DN

searchBase

The DN of the base to start the search for the user account

searchAttribute

The name of the attribute to search for

VirtualMemberOf

This insert creates a virtual attribute for storing the DNs of objects that reference this object, such as the common memberof attribute. This attribute is read-only and can be searched on in a filter.

Class Name

net.sourceforge.myvd.inserts.mapping.VirtualMemberOf

Scope

Search, Post Search Entry

Configuration Options

searchBase

Where to start search for referencing objects

applyToObjectClass

The name of the objectClass to add the virtual attribute to

attributeName

The name of the virtual attribute

searchObjectClass

The objectClass of referencing objects

searchAttribute

The referencing attribute

replace

If true (default) and existing attribute is replaced

DeleteAttribute

This insert will delete an attribute from an entry.

Class Name

net.sourceforge.myvd.inserts.mapping.DeleteAttribute

Scope

Add, Post Search Entry

Configuration Options

attributes

Comma "," sepperated list of attributes to remove

Joining Inserts

The joining inserts can be combined to join entries across several directories and databases. In addition to the joiner insert there are several inserts that assist with the utilization of joining.

Joiner

The joiner is used to combine two LDAP namespaces (note that this means two DN namespace, not two configuration namespaces). The joiner combines entries from each namespace.

Class Name

net.sourceforge.myvd.inserts.join.Joiner

Scope

Search

Configuration Options

primaryNamespace

LDAP DN that represents a configured namespace that will drive the namespace of the joiner

joinedNamespace

LDAP DN that represents a configured namespace that will be joined with the primaryNamespace. The namespace of the primaryNamespace drives the joiner’s namespace

joinedAttributes

Attributes to be included in the join

joinFilter

A filter that is used to combine objects. When including an attribute from the joining entry prepend the attribute with "ATTR.". For instance, to combine a user based on the uid attribute the filter would be "(uid=ATTR.uid)"

bindPrimaryFirst

If set to true, MyVD will first try to bind the primary insert. If set to false it will try the joined insert

Joiner - Simple Join Modify

The simple join modify insert is configured AFTER a joiner on a chain in order to support the modification of joined attributes

Class Name

net.sourceforge.myvd.inserts.join.SimpleJoinModify

Scope

Modify

Configuration Options

joinerName

The name of the joiner insert configured on this chain

Joiner - Join Add Flat NameSpace

The join add flat namespace insert is designed to allow the addition of entries to a joiner namespace. NOTE: this insert MUST be configured as the last insert on a chain.

Class Name

net.sourceforge.myvd.inserts.join.JonAddFlatNS

Scope

Add

Configuration Options

joinerName

The name of the joiner insert configured on this chain

joinedObjectClass

The objectclass for joined entries

sharedAttributes

Comma seperated list of attributes shared by both the primary and joined namespaces

Directory Inserts

Directory inserts relate to the connection to directory and directory-like services and mapping the data from those services.

LDAP Insert

The LDAP insert is one of the core inserts. This insert allows MyVD to communicate with any LDAPv3 compliant directory. In addtion to LDAP directories, this insert supports DSMLv2 and SPML. In order to take advantage of the SPML feautures you must download the OpenSPML toolkit (http://www.openspml.org/) and it’s dependencies.

Class Name

net.sourceforge.myvd.inserts.ldap.LDAPInterceptor

Scope

Add, Modify, Rename, Delete, Compare, Search, Bind, Extended Operations

Configuration Options

host

The host of the remote server. If this insert is using DSMLV2 or SPML then this is the URL of the end point.

port

The port the remote ldap server is listening on. Ignored for DSMLV2 and SPML

remoteBase

The base of the remote LDAP server that the insert would start at

minimumConnections

The minimum number of connections to keep in the pool

maximumConnections

The maximum number of connections to keep in the pool

proxyDN

The DN of the user to connect as

proxyPass

The password of the remote directory

type

One of "ldap","ldaps","dsmlv2" or "spml"

spmlImpl

When using SPML, the class name for the com.novell.ldap.spml.SPMLImpl interface

passBindOnly

"true" or "false" if the user’s credentials should be utilized for only the "bind" process. "false" if the credentials should be used on all operations.

ignoreRefs

"true" if referrals should be ignored.

maxMillis

The maximum number of millis seconds an operation can take. Defaults to 30,000 (30 seconds)

maxStaleTimeMillis

The maximum number of milliseconds a connection can be locked before its considered stale. Default is 60,000 (60 seconds)

heartbeatIntervalMillis

The interval in milliseconds to perform a heart beet for pool connections. 0 means do not run a heartbeat

Kerberos Authenticator

The kerberos authenticator can be used to authenticate users via the Kerberos protocol, such as against Active Directory. Note that all configuration is based on the Java JNDI kerberos authentication file. (need to add a link)

Class Name

net.sourceforge.myvd.inserts.kerberos.KerberosInterceptor

Scope

Bind

Configuration Options

None

NTLM Authenticator

The kerberos authenticator can be used to authenticate users via the NTLM protocol, such as against Active Directory or an NT4 Domain Controller.

Class Name

net.sourceforge.myvd.inserts.jcifs.NTLMAuthenticator

Scope

Bind

Configuration Options

host

The host of the NTLM server (AD or an NTLM file share)

Dynamic Groups Insert

The Dynamic Groups Insert allows for a Dyanmic Group (groupOfUrls) to act like a static group in the following ways:

  1. All dynamic members can be listed as static members

  2. You can test if a user is a member of the group by testing if the user is a static member

This all allows for applications that are not capable of using dynamic groups while still providing the maintenance benefits of static groups.

Class Name

net.sourceforge.myvd.inserts.ldap.Dynamicgroups

Scope

Search

Configuration Options

dynamicObjectClass

The object class for dynamic groups (typically groupOfUrls)

staticObjectClass

The object class for static groups (typically groupOfUniqueNames)

urlAttribute

The attribute that stores the LDAP url (typically memberUrl)

staticAttribute

The attribute that stores static members (typically uniqueMember)

Embedded Groups

The Embedded Groups insert allows for static groups to contain members that are other groups to be tested with a user’s DN. For instance:

dn: cn=Child Group,ou=groups,dc=domain,dc=com
objectClass: groupOfUniqueNames
cn: Child Group
uniqueMember: cn=Test User,ou=people,dc=domain,dc=com
.
.
.

dn: cn=Parent Group,ou=groups,dc=domain,dc=com
objectClass: groupOfUniqueNames
cn: Parent Group
uniqueMember: cn=Child Group,ou=groups,dc=domain,dc=com
.
.
.

The above example would typically require two searches in order to test cn=Test User,ou=people,dc=domain,dc=com:

  1. Retrieve the group

  2. Determine which members are groups, and search each group for the user cn=Test User,ou=people,dc=domain,dc=com

The Embedded Groups does this work for you, tracking which members are groups to determine what groups need to be searched. The insert can track which members are groups either by polling the remote directory periodically or (if the directory supports it) by using persistent search to constantly track the addition or removal of groups.

Class Name

net.sourceforge.myvd.inserts.ldap.EmbeddedGroups

Scope

Search

Configuration Options

groupSearchBase

The search base in the remote directory to look for groups

staticObjectClass

The object class for static groups (typically groupOfUniqueNames)

staticAttribute

The attribute that stores static members (typically uniqueMember)

userDN

The DN used to connect to the remote directory

userPwd

The password used to connect to the remote directory

useSync

true or false / True if MyVD should use persistent search to determine group DNs, false if the directory should be polled

Static Bind DN Map

The Static Bind DN Map insert can perform a static mapping from an external DN to a DN for a remote directory. Typically this insert would be used to map from DNs that are specific for an insert’s base to a non-specific base. Such as when using Sun/Fedora Directory the root user is typically cn=Directory Manager which has no base, so this insert could map uid=admin,dc=domain,dc=com→cn=Directory Manager.

Class Name

net.sourceforge.myvd.inserts.ldap.StaticDNMap

Scope

Add,Modify,Delete,Bind,Search,Extended Operation

Configuration Options

dnmap

Pipe  u007C separated list of carrot separated DNs, external to internal (ie uid=admin,dc=domain,dc=comcn=Directory Manager)

Database Inserts

These inserts are meant for integrating MyVD with relational databases. The primary insert for integrating with databases is the JDBC Insert with additional inserts being used for mapping and database updates.

JDBC Insert

The JDBC insert is used to expose a relational database through MyVD. This insert only supports a single objectClass and rdn for each configuration of the insert.

Class Name

net.sourceforge.myvd.inserts.jdbc.JdbcInsert

Scope

Search

Configuration Options

driver

The JDBC driver to use

url

The JDBC connection URL

user

The database user to connect as

password

The password to connect with

rdn

The LDAP atribute used as the rdn for all objects generated

mapping

Comma sepperated list of ldap=db mapping of attributes

objectClass

The objectClass for all octs created by this insert

sql

The SQL use to determine what objects this insert will generate. This SQL must include all attributes included in the mapping configuration

maxCons

The maximum number of connections, default is 5

useSimple

true or false, utilizes the "simple" SQL, mutch better performance but only the value of multivalued attributes in the filter are returned

validationQuery

(optional) A SQL query that will be used to validate connections. If specified, this query MUST be an SQL SELECT statement that returns at least one row.

addBaseToFilter

(optional)

idleConnectionTestPeriod

(optional - default is 30) Number of seconds unchecked out connections are tested

unreturnedConnectionTimeout

(optional - defaults to 0) Number of seconds a connection can checked out before the connection is closed and returned to the pool. 0 means it will not be forced.

checkoutTimeout

(optional - defaults to 30000 milliseconds) Number of milliseconds to wait for a connection to be available.

Database Table Update

This insert contains the logic to update a single database table. It is configured BEHIND a database insert in order to utilize that insert’s connection pool and mapping.

Class Name

net.sourceforge.myvd.inserts.jdbc.DBTableUpdate

Scope

Add,Modify,Delete,Rename

Configuration Options

tableName

The name of the table to update in the database

dbInsertName

The name of the insert used by this insert

DB Groups

This insert is used when groups stored in a relational database are stored with a name and not a full DN. This plugin will create the DN to make it LDAP compliant.

Class Name

net.sourceforge.myvd.inserts.jdbc.DBGroups

Scope

Search

Configuration Options

memberAttribute

The name of the attribute that represents members (ie uniqueMember)

suffix

The member suffix (ie ou=groups,dc=domain,dc=com

rdn

The name of the rdn attribute (ie uid)

Composite Attribute Insert

Often with relational databases a full, or "common", name is not stored as a single field but is rather stored as a first, or given, name and a last, or sur, name. Since LDAP typically expects there to be a composite attribute that has both pieces in a single attribute. This insert can also be used to create user ids based on a person’s name.

Class Name

net.sourceforge.myvd.inserts.jdbc.CompositeAttrib

Scope

Search

Configuration Options

attribute

The name of the final composite attribute

components

Comma sperated list with the following format : "attribute:[#

properCase

true/false, true if the composite attribute should be forced to lowercase

objectClass

The objectClass for objects that the mapping should be performed on

Simple DB Authentication

By default the JdbcInsert does not support authentication. This insert, configured BEFORE the JdbcInsert allows a a simple authentication using an un-salted one-way hash.

Class Name

net.sourceforge.myvd.inserts.jdbc.SimpleDBAuth

Scope

Bind

Configuration Options

sql

A SQL SELECT statement with two paramters. The first parameter maps the bind DN’s RDN and the second is the hashed password

hashFunction

Eith "SHA" or "MD5" without quotes

PBKDF2 DB Authentication

By default the JdbcInsert does not support authentication. This insert, configured BEFORE the JdbcInsert supports LDAP binds against a password stored using the PBKDF2 format created by net.sourceforge.myvd.util.PBKDF2.generateHash.

Class Name

net.sourceforge.myvd.inserts.jdbc.Pbkdf2Auth

Scope

Bind

Configuration Options

sql

A SQL SELECT statement with one parameter, the user’s rdn, and returns a the password in the only column to be returned. Example SELECT password FROM Users WHERE username=?

Web Services Inserts

These inserts assist with integration with web services. Currently the only web services supported by MyVD are DSMLv2 and SPML via the LDAP Insert. The below inserts assist with integrating with those services.

Create SPML Identity

The SPML identity insert is designed to create an LDAP compliant DN for use with the LDAP insert in SPML mode. SPML does not require a full DN as a name (it can for instance use a userid or an email address), but LDAP does. This insert ensures that the DN for the user object has the correct RDN for SPML while being LDAP compliant.

Class Name

net.sourceforge.myvd.inserts.idm.CreateSPMLIdentity

Scope

Add, Modify, Delete

Configuration Options

type

The SPML identity type (ie GenericString

attribute

The attribute that will act as the source for the RDN data

keepNameAsAttribute

true or false if the rdn’s attribute will remain on the user object

Flatten Namespace

This insert removes components from an LDAP DN and can optionally store them in the user entry.

Class Name

net.sourceforge.myvd.inserts.idm.FlattenNamespace

Scope

Add, Delete

Configuration Options

attribsToRemove

Comma sepperated list of attributes to remove from the DN

attributesToStore

Comma sepperated list of attributes in the DN to store in the user entry

Active Directory Inserts

These inserts help integrate MyVD with Active Directroy. They provide services to help integrate applications with Active Directory to best utilize Active Directory and simplify integration utilizing MyVirtualDirectory.

ObjectGUID & ObjectSID To String

Nearly every object in Active Directory contains both an Object GUID and an Object SID. The GUID is a unique identifier in Active Directory, with the SID being a security identifier. These are both binary attributes that are easiest to represent as strings. This insert takes the binary version of the these attributes and re0works them to present the string based representation. It also takes the string based representation in filters and translates it to a binary version.

Class Name

net.sourceforge.myvd.inserts.ad.ObjectGuidToString

Scope

Search, Post Search Entry

Configuration Options

Primary Group ID

Every user has a primary group. This group is noted in the Active Directory attribute primaryGroupID, which references the last component of the of the primary group’s objectsid attribute. Users are not listed as members of their primary group. This insert, when combined with the "ObjectGUID & ObjectSID to String" insert beneath it and the DynamicGroups insert above it will list all members of a group, including those who note the group as its primary group.

Class Name

net.sourceforge.myvd.inserts.ad.PrimaryGroup

Scope

Search, Post Search Entry

Configuration Options

searchBase

The base to search for users, included in the url appended to all group entries

groupObjectClass

The object class for groups, typically "group"

Generate Posix GID

This insert helps to integrate ActiveDirectory with posix systems. It is configured on the same chain as a joiner and provides the following functions:

  1. Generate a users gidnumber based on the primaryGroupID

  2. If a user or group exists in Active Directory but not in the database used to store posix attributes it creates the entry

Class Name

net.sourceforge.myvd.inserts.ad.GeneratePosixGID

Scope

Search, Post Search Entry

Configuration Options

userIdAttribute

The user id number attribute, by default uidNumber

groupIdAttribute

The group id number attribute, by default gidNumber

userObjectClass

The object class that identifies users

groupObjectClass

The object class that identifies groups

userAddBase

The base for joined users

groupAddBase

The base for joined adds

homeDirTemplate

The template for home directories. Enclose attributes in @@. For instance to have the directory be /home/USERS_SAMACCOUNTNAME it would be /home/@samaccountname@

Active Directory Insert

The active directory insert takes a Microsoft Active Directory and makes it appear to be a standard inetOrgPerson type directory. This insert utilizes the LDAP insert in order to communicate with Active Directory and includes additional mapping functions:

  1. DN Mapping

  2. ObjectGUID and ObjectSID mapping

  3. Embedded Groups

  4. Primary Group Listings

Class Name

net.sourceforge.myvd.inserts.ad.ADInsert

Scope

Add, Modify, Delete, Rename, Search, Post Search Entry

Configuration Options

ADBase

The domain’s base

searchDn

The user used to search AD. This user should not have an search limit restrictions

searchPwd

The password for the search user

remoteBase

The remote base for dn mapping

userAddBase

The base for joined users

groupAddBase

The base for joined adds

homeDirTemplate

The template for home directories. Enclose attributes in @@. For instance to have the directory be /home/USERS_SAMACCOUNTNAME it would be /home/@samaccountname@

host

The host of the remote server. If this insert is using DSMLV2 or SPML then this is the URL of the end point.

port

The port the remote ldap server is listening on. Ignored for DSMLV2 and SPML

remoteBase

The base of the remote LDAP server that the insert would start at

minimumConnections

The minimum number of connections to keep in the pool

maximumConnections

The maximum number of connections to keep in the pool

proxyDN

The DN of the user to connect as

proxyPass

The password of the remote directory

type

One of "ldap","ldaps","dsmlv2" or "spml"

spmlImpl

When using SPML, the class name for the com.novell.ldap.spml.SPMLImpl interface

passBindOnly

"true" or "false" if the user’s credentials should be utilized for only the "bind" process. "false" if the credentials should be used on all operations.

ignoreRefs

"true" if referrals should be ignored.

Posix DB

While this insert isn’t directly associated with Active Directory, when combined with the Active Directory Joiner and the Active Directory insert this insert provides additional posix attributes.

Class Name

net.sourceforge.myvd.inserts.ad.PosixDB

Scope

Add, Modify, Delete, Rename, Search, Post Search Entry

Configuration Options

tableName

The name of the posix table

type

user or group

Configuration Options

driver

The JDBC driver to use

url

The JDBC connection URL

user

The database user to connect as

password

The password to connect with

maxCons

The maximum number of connections, default is 5

maxIdleCon

The maximum amount of time a connection can remain idle before closing

AD Posix Joiner

The Active Directory / Posix Joiner is an insert that combines the general joiner with the mapping inserts and Active directory inserts in order to create a view for posix integration. This joiner is meant to join a namespace configured with the AD Insert and another namespace configured for users and groups using the PosixDB inserts.

Class Name

net.sourceforge.myvd.inserts.ad.ADPosixJoiner

Scope

Search, Post Search Entry

Configuration Options

activeDirectoryBase

The base for the AD inserts

dnAttribs

Optional, AD attributes. Default uniqueMember,member,memberOf,distinguishedname,objectcategory

userAddBase

The RDN of the user base

groupAddBase

The object class that identifies groups

homeDirTemplate

The template for home directories. Enclose attributes in @@. For instance to have the directory be /home/USERS_SAMACCOUNTNAME it would be /home/@samaccountname@

dbBase

The base for the PosixDB inserts

CorruptObjectGUID

This insert allows a client that tries to search on an ObjectGUID that has been cast to text improperly.

Class Name

com.tremolosecurity.proxy.myvd.inserts.util.CorruptObjectGUID

Scope

Search, Post Search Entry

CreateUPN

Active Directory “user” objects don’t all have user principal name objects which can interfere with directory based systems that expect them. This insert will create a userPrincipalName object based on a directory attribute and suffix.

Class Name

com.tremolosecurity.proxy.myvd.inserts.ad. CreateUPN

Scope

Search, Post Search Entry

Configuration Options

prefixAttributeName

The name of the attribute that’s used as the source for the UPN; generally uid

suffix

The domain name to use as a suffix for the UPN

UUIDtoText

The objectGUID attribute is a binary attribute that is often corrupted by translation to text. This insert will translate a binary attribute to text properly.

Class Name

com.tremolosecurity.proxy.myvd.inserts.util.UUIDtoText

Scope

Search, Post Search Entry

Configuration Options

attributeName

The name of the attribute to map

AddActiveDirectoryEnabledFlag

This insert will check the userAccountControl in AD for the value of "2" and set an attribute to "true" if the user is enabled and "false" if the user is disabled.

Class Name

net.sourceforge.myvd.inserts.mapping.AddActiveDirectoryEnabledFlag

Scope

Search, Post Search Entry

Configuration Options

nameOfAttributeToCreate

The name of the attribute to store the flag in

Creating Custom Inserts

MyVD supports the creation of custom inserts. Technically speaking, there is no difference between a "custom" insert and one that comes with MyVD. Inserts are pretty straight forward to build and only require some knowledge of LDAP, Java and whatever you are looking to integrate. Inserts work on a similar premesise as Java Servlet filters. A single method is used for both pre, post and operation control.

Why Create a Custom Insert?

There are many reasons why you would create a custom insert. These range from integrating a resource that is not currently supported by MyVD (such as a web service) to needing to implement more complex routing rules then existing Inserts provide.

Creating a Development Environment

MyVD requires the following for developing a custom insert:

  • JDK 1.5+

  • myvd.jar

  • ldap.jar

  • jdbcldap.jar

As for how you develop and integrate your custom insert, thats up to you.

Getting Started

Once your development environment is setup, creating an insert only requires that you implement the net.sourceforge.myvd.inserts.Insert interface. A bare bones insert might look like this:

package net.sourceforge.myvd;

import java.util.ArrayList;
import java.util.Properties;

import com.novell.ldap.LDAPConstraints;
import com.novell.ldap.LDAPException;
import com.novell.ldap.LDAPModification;
import com.novell.ldap.LDAPSearchConstraints;

import net.sourceforge.myvd.chain.AddInterceptorChain;
import net.sourceforge.myvd.chain.BindInterceptorChain;
import net.sourceforge.myvd.chain.CompareInterceptorChain;
import net.sourceforge.myvd.chain.DeleteInterceptorChain;
import net.sourceforge.myvd.chain.ExetendedOperationInterceptorChain;
import net.sourceforge.myvd.chain.ModifyInterceptorChain;
import net.sourceforge.myvd.chain.PostSearchCompleteInterceptorChain;
import net.sourceforge.myvd.chain.PostSearchEntryInterceptorChain;
import net.sourceforge.myvd.chain.RenameInterceptorChain;
import net.sourceforge.myvd.chain.SearchInterceptorChain;
import net.sourceforge.myvd.core.NameSpace;
import net.sourceforge.myvd.inserts.Insert;
import net.sourceforge.myvd.types.Attribute;
import net.sourceforge.myvd.types.Bool;
import net.sourceforge.myvd.types.DistinguishedName;
import net.sourceforge.myvd.types.Entry;
import net.sourceforge.myvd.types.ExtendedOperation;
import net.sourceforge.myvd.types.Filter;
import net.sourceforge.myvd.types.Int;
import net.sourceforge.myvd.types.Password;
import net.sourceforge.myvd.types.Results;

public class WebServiceInsert implements Insert {

        public void add(AddInterceptorChain chain, Entry entry,
                        LDAPConstraints constraints) throws LDAPException {
                //TODO Add pre-operation code

                chain.nextAdd(entry, constraints);

                //TODO Add post-operation code

        }

        public void bind(BindInterceptorChain chain, DistinguishedName dn,
                        Password pwd, LDAPConstraints constraints) throws LDAPException {
                //TODO Add pre-operation code

                chain.nextBind(dn, pwd, constraints);

                //TODO Add post-operation code

        }

        public void compare(CompareInterceptorChain chain, DistinguishedName dn,
                        Attribute attrib, LDAPConstraints constraints) throws LDAPException {
                //TODO Add pre-operation code

                chain.nextCompare(dn, attrib, constraints);

                //TODO Add post-operation code
        }

        public void configure(String name, Properties props, NameSpace nameSpace)
                        throws LDAPException {
                //TODO Write Configuration Code Here

        }

        public void delete(DeleteInterceptorChain chain, DistinguishedName dn,
                        LDAPConstraints constraints) throws LDAPException {
                //TODO Add pre-operation code

                chain.nextDelete(dn, constraints);

                //TODO Add post-operation code
        }

        public void extendedOperation(ExetendedOperationInterceptorChain chain,
                        ExtendedOperation op, LDAPConstraints constraints)
                        throws LDAPException {
                //TODO Add pre-operation code

                chain.nextExtendedOperations(op, constraints);

                //TODO Add post-operation code

        }

        public void modify(ModifyInterceptorChain chain, DistinguishedName dn,
                        ArrayList<LDAPModification> mods, LDAPConstraints constraints)
                        throws LDAPException {
                //TODO Add pre-operation code

                chain.nextModify(dn, mods, constraints);

                //TODO Add post-operation code

        }

        public void postSearchComplete(PostSearchCompleteInterceptorChain chain,
                        DistinguishedName base, Int scope, Filter filter,
                        ArrayList<Attribute> attributes, Bool typesOnly,
                        LDAPSearchConstraints constraints) throws LDAPException {
                //TODO Add pre-operation code

                chain.nextPostSearchComplete(base, scope, filter, attributes, typesOnly, constraints);

                //TODO Add post-operation code

        }

        public void postSearchEntry(PostSearchEntryInterceptorChain chain,
                        Entry entry, DistinguishedName base, Int scope, Filter filter,
                        ArrayList<Attribute> attributes, Bool typesOnly,
                        LDAPSearchConstraints constraints) throws LDAPException {
                //TODO Add pre-operation code

                chain.nextPostSearchEntry(entry, base, scope, filter, attributes, typesOnly, constraints);

                //TODO Add post-operation code

        }

        public void rename(RenameInterceptorChain chain, DistinguishedName dn,
                        DistinguishedName newRdn, Bool deleteOldRdn,
                        LDAPConstraints constraints) throws LDAPException {
                //TODO Add pre-operation code

                chain.nextRename(dn, newRdn, deleteOldRdn, constraints);

                //TODO Add post-operation code

        }

        public void rename(RenameInterceptorChain chain, DistinguishedName dn,
                        DistinguishedName newRdn, DistinguishedName newParentDN,
                        Bool deleteOldRdn, LDAPConstraints constraints)
                        throws LDAPException {
                //TODO Add pre-operation code

                chain.nextRename(dn, newRdn, newParentDN, deleteOldRdn, constraints);

                //TODO Add post-operation code
        }

        public void search(SearchInterceptorChain chain, DistinguishedName base,
                        Int scope, Filter filter, ArrayList<Attribute> attributes,
                        Bool typesOnly, Results results, LDAPSearchConstraints constraints)
                        throws LDAPException {
                //TODO Add pre-operation code

                chain.nextSearch(base, scope, filter, attributes, typesOnly, results, constraints);

                //TODO Add post-operation code

        }

}

Each method has essentially the same pattern:

\1. Pre-operation work (ie adding attributes or routing)

\2. Continue down the operations chain

\3. Post-operation work

If this insert is going to handle the request then don’t call the methods "nextXXX" method. The "configure" method is called when the insert is initiated. The "props" object contains all properties configured for this insert.

Accessing and Updating the Virtual Directory

Sometimes it’s desirable to perform a search or update an object in the directory inside of an insert. Inserts may create new chains of execution via the "createXXX" methods on the chain object.

chain.createAddChain().nextAdd(entry, constraints);

This code will execute every insert below the current one. So for instance if this insert is the 3rd in a chain of five inserts the above code would execute inserts four and five.

Using SQL inside of MyVD

One feature that only exists in MyVD is to utilize the JDBC-LDAP bridge to use SQL inside of MyVD instead of coding what is needed uing the above method. This is useful for Java developers who are more familiar with SQL then LDAP. A connection object can be retrieved via the chain object.

java.sql.Connection con = chain.createJdbcLdapConnection();

Deploying Custom Inserts

Once the insert is complete, jar it and copy it to the "lib" directory of your MyVD installation. Then you can configure your insert like any other insert.

Deploying MyVD

MyVD Prerequisites

MyVD require JRE 1.8 or higher. All other pre-requisites come with MYVD.

The MyVD Install Directory

Deploying MyVD is fairly straight forward. The download bundle has several directories:

Directory

Contents

bin

Contains all startup scripts

conf

Comtains the myvd.conf file and the logging.conf files

logs

Default log location

lib

Stores all additional libraries. This is where to copy jar files containing custom inserts

jar

Contains the myvd.jar file

Starting MyVD

The bin directory contains unix and windows startup scripts. To start MyVD on windows, double click (or create a shortcut to) myvd.vbs. On unix run the myvd.sh script with either the parameters "start" or "stop".

If you wish to create a custom start script the MyVD startup command is

java -server net.sourceforge.myvd.server.Server /path/to/config/myvd.conf

The Default MyVD Configuration

The myvd.conf file in the conf directory of the MyVD bundle has a very basic default configuration. This configuration is setup to have the DumpTransaction insert on the global chain and a sample RootDSE configuration.

Configuring Logging

MyVD utilizes the Log4j2 (http://logging.apache.org/log4j/2.x/) logging system. By default MyVD will log to the "logs" directory in the MyVD bundle at an "informational" level. The full default Log4J configuration is

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
        <Appenders>
                <RollingFile name="server-log" fileName="../logs/myvd.log"
                        append="true" filePattern="../logs/myvd.log.%d">
                        <PatternLayout pattern="[%d][%t] %-5p %c{1} - %m%n" />
                        <Policies>
                                <TimeBasedTriggeringPolicy />
                        </Policies>
                </RollingFile>
        </Appenders>
        <Loggers>

                <Root level="info">
                        <AppenderRef ref="server-log" />
                </Root>
        </Loggers>
</Configuration>

To customize logging log4j properties may be set in a file called log4j2.xml in the same directory as the myvd.conf file used to configure your instance. If you only wish to make a change to any of the above properties you only need to set that property. For instance to enable "debug" mode the log4j2.xml file should look like:

l<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
        <Appenders>
                <RollingFile name="server-log" fileName="../logs/myvd.log"
                        append="true" filePattern="../logs/myvd.log.%d">
                        <PatternLayout pattern="[%d][%t] %-5p %c{1} - %m%n" />
                        <Policies>
                                <TimeBasedTriggeringPolicy />
                        </Policies>
                </RollingFile>
        </Appenders>
        <Loggers>

                <Root level="debug">
                        <AppenderRef ref="server-log" />
                </Root>
        </Loggers>
</Configuration>

Utilizing The Start Scripts

MyVD comes with start scripts for both *nix and Windows. In order to use these scripts two environment variables need to be defined:

JAVA_HOME

The path to the JRE

MYVD_HOME

The path to where MyVD has been installed

Integrating Directories

Directory integration is one of the simplest tasks in MyVD. In that MyVD is built and organized like a directory, integrating a directory is very simple and straight forward. There are several possible use cases for integrating directories such as:

  1. Directory Integration - Combining multiple directories into one larger directory

  2. DN Re-writing - A directory needs to be re-organized for a specific application or the RDN needs to be changed but the underlying data can not be changed

  3. Attribute Mapping - An application requires attribute names to be changed or attributes to be created

  4. Master / Replica Management - Many directory environments have a master/replica setup where writes should be sent to a replica while reads are executed against a replica transparently to applications

MyVD supplies several inserts to support these use cases. The primary insert for directory integration is the LDAP Insert.

Configuring a basic LDAP Proxy

The LDAP Insert is the most important insert for integrating directories as it acts as the primary interface between a remote directory and MyVD. In order to setup a simple proxy all one must do is create an LDAP insert. Below is the configuration information for the LDAP insert:

LDAP Insert

The LDAP insert is one of the core inserts. This insert allows MyVD to communicate with any LDAPv3 compliant directory. In addtion to LDAP directories, this isnert supports DSMLv2 and SPML. In order to take advantage of the SPML feautures you must download the OpenSPML toolkit (http://www.openspml.org/) and it’s dependencies.

Class Name

net.sourceforge.myvd.inserts.ldap.LDAPInterceptor

Scope

Add, Modify, Rename, Delete, Compare, Search, Bind, Extended Operiations

Configuration Options

host

The host of the remote server. If this insert is using DSMLV2 or SPML then this is the URL of the end point.

port

The port the remote ldap server is listening on. Ignored for DSMLV2 and SPML

remoteBase

The base of the remote LDAP server that the insert would start at

minimumConnections

The minimum number of connections to keep in the pool

maximumConnections

The maximum number of connections to keep in the pool

proxyDN

The DN of the user to connect as

proxyPass

The password of the remote directory

type

One of "ldap","dsmlv2" or "spml"

spmlImpl

When using SPML, the class name for the com.novell.ldap.spml.SPMLImpl interface

passBindOnly

"true" or "false" if the user’s credentials should be utilized for only the "bind" process. "false" if the credentials should be used on all operations.

ignoreRefs

"true" if referals should be ignored.

The above configuration parameters are for the most part very straight forward. The host and port configs are the host and port the directory is running on. The remoteBase config is to determine where in the remote directory MyVD should connect to. THe minimumConnections and maximumConnections are for connection pooling. The proxyDN and proxyPass configuration options may be supplied if you do not want to proxy the user’s credentials. When these are set they are used for all operations except for the bind operation. The type parameter is used to determine the protocol type. The LDAP Insert is based on Novel’s JLDAP implementation which supports LDAPv3, DSMLv2 (LDAP Web Service) and SPML (provisioning Web Service). Finaly, the spmlImpl property is used to determine how an SPML insert should interact with the remote server. By simply seting up an LDAP insert using the above parameters you can easily setup a remote proxy.

The below configuration is from the unit tests and shows a very simple proxy:

server.listener.port=50983

#No global chain
server.globalChain=

#Setup a single proxy
server.nameSpaces=BaseServer

#Simple LDAP insert
server.BaseServer.chain=LDAPBaseServer
server.BaseServer.nameSpace=o=mycompany,c=us
server.BaseServer.weight=0
server.BaseServer.LDAPBaseServer.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.BaseServer.LDAPBaseServer.config.host=localhost
server.BaseServer.LDAPBaseServer.config.port=10983
server.BaseServer.LDAPBaseServer.config.remoteBase=dc=domain,dc=com
server.BaseServer.LDAPBaseServer.config.proxyDN=cn=admin,dc=domain,dc=com
server.BaseServer.LDAPBaseServer.config.proxyPass=manager

The above configuration is very straight forward. A single insert is created for an ldap server running on the local server with administrative credentials. While the above example is simple, it is only the tip of the iceberg.

Integrating Multiple Directories

It’s common for an IT infrastructure to have multiple directories, not just a single directory. In many situations there are at least two directories, one used for internal users (such as employees) and another for external users such as contractors and customers. While this may make maintenance easier, most applications can only utilize a single directory however. MyVD can be used to integrate these multiple directories into a single directory without copying data. The below diagram shows how this may be accomplished:

Namespace Integration

Integrating multiple directories is very easy with MyVD. In order to perform this integration simply create seperate inserts for each directory with seperate DNs for the base. The below example configuration shows how to accomplish this with three inserts:

  1. Base - For the user base (required by some applications)

  2. Internal Users - For all internal users

  3. External Users - For all external users

server.listener.port=50983
server.globalChain=

#Define a namespace for each part of the DIT
server.nameSpaces=BaseServer,InternalServer,ExternalServer

#define the base server to provide an entry to connect internal and external users
server.BaseServer.chain=LDAPBaseServer
server.BaseServer.nameSpace=o=mycompany,c=us
server.BaseServer.weight=0
server.BaseServer.LDAPBaseServer.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.BaseServer.LDAPBaseServer.config.host=localhost
server.BaseServer.LDAPBaseServer.config.port=10983
server.BaseServer.LDAPBaseServer.config.remoteBase=dc=domain,dc=com
server.BaseServer.LDAPBaseServer.config.proxyDN=cn=admin,dc=domain,dc=com
server.BaseServer.LDAPBaseServer.config.proxyPass=manager

#Create the chain for internal users
server.InternalServer.chain=LDAPInternalServer
server.InternalServer.nameSpace=ou=internal,o=mycompany,c=us
server.InternalServer.weight=10
server.InternalServer.LDAPInternalServer.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.InternalServer.LDAPInternalServer.config.host=localhost
server.InternalServer.LDAPInternalServer.config.port=11983
server.InternalServer.LDAPInternalServer.config.remoteBase=ou=internal,dc=domain,dc=com
server.InternalServer.LDAPInternalServer.config.proxyDN=cn=admin,ou=internal,dc=domain,dc=com
server.InternalServer.LDAPInternalServer.config.proxyPass=manager

#Create the chain for external users
server.ExternalServer.chain=LDAPExternalServer
server.ExternalServer.nameSpace=ou=external,o=mycompany,c=us
server.ExternalServer.weight=15
server.ExternalServer.LDAPExternalServer.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.ExternalServer.LDAPExternalServer.config.host=localhost
server.ExternalServer.LDAPExternalServer.config.port=12983
server.ExternalServer.LDAPExternalServer.config.remoteBase=ou=external,dc=domain,dc=com
server.ExternalServer.LDAPExternalServer.config.proxyDN=cn=admin,ou=external,dc=domain,dc=com
server.ExternalServer.LDAPExternalServer.config.proxyPass=manager

The above configuration is fairly straight forward. Once created, a virtual directory representing both internal and external users is created. This configuration can further be extended by adding replica routing inserts, access controls and joiners.

Directory Routing

Many directory environments include seperate servers for read operations and write operations. Because applications aren’t generaly coded to work in this way, MyVD can be setup as a router to route requests to difference name spaces based on certain criteria (in this case the type of operation being performed). An insert called the MasterReplicaInsert may be used to automatically route write requests to masters while routing all search and bind requests to replicas. The below configuration shows how this concept would be implemented.

server.listener.port=50983
server.globalChain=routingPlugin



#routing plugin to forward all writes to the master and reads to the "replicas"
server.globalChain.routingPlugin.className=net.sourceforge.myvd.inserts.routing.MasterReplicaRouter
server.globalChain.routingPlugin.config.specifyToInclude=false
server.globalChain.routingPlugin.config.readOnly=BaseServer
server.globalChain.routingPlugin.config.master=MasterServer


server.nameSpaces=MasterServer,BaseServer

#Define the master
server.MasterServer.chain=LDAPBaseServer
server.MasterServer.nameSpace=o=mycompany,c=us
server.MasterServer.weight=0
server.MasterServer.LDAPBaseServer.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.MasterServer.LDAPBaseServer.config.host=localhost
server.MasterServer.LDAPBaseServer.config.port=13983
server.MasterServer.LDAPBaseServer.config.remoteBase=dc=domain,dc=com
server.MasterServer.LDAPBaseServer.config.proxyDN=cn=admin,dc=domain,dc=com
server.MasterServer.LDAPBaseServer.config.proxyPass=manager

#Define the replica with the same namespace as the master
server.BaseServer.chain=LDAPBaseServer
server.BaseServer.nameSpace=o=mycompany,c=us
server.BaseServer.weight=10
server.BaseServer.LDAPBaseServer.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.BaseServer.LDAPBaseServer.config.type=LDAP
server.BaseServer.LDAPBaseServer.config.host=localhost
server.BaseServer.LDAPBaseServer.config.port=10983
server.BaseServer.LDAPBaseServer.config.remoteBase=dc=domain,dc=com
server.BaseServer.LDAPBaseServer.config.proxyDN=cn=admin,dc=domain,dc=com
server.BaseServer.LDAPBaseServer.config.proxyPass=manager

By using the master/replica routing insert applications can transparently interact with the directory environment even if the application is not built to handle it.

Using Databases

Its very common to utilize a virtual directory to integrate relational databases into a directory infrastructure. There are two primary ways to integrate a database:

  • Store additional application specific attributes

  • Expose existing data in a relational database as a directory

In both of these situations integrating your existing database infrastructure into MyVirtualDirectory is very easy. Before going over how to integrate a database, lets cover some of the fundamentals of integrating with databases:

This first thing to understand is how MyVD integrates a database. There are three primary configuration points:

  1. DB Configuration Information - How does MyVD connect to the database?

  2. Data SQL - What data will MyVD be serving? This is configured as an SQL statement. It can be as simple as "SELECT * FROM table" and can be as complex as you want. Its not recomended that a very complex SQL statement is used for performance reasons.

  3. Mapping - Map database column names to ldap attribute names.

Once you have decided what data and how it will be mapped, you need to decide if you want writes to be sent to the database. The database insert alone does not support writes. We will cover to perform writes later in the doc.

Setting Up a Simple Database Insert

First lets setup a simple database insert. The database insert has the following configuration

Class Name

net.sourceforge.myvd.inserts.jdbc.JdbcInsert

Scope

Search

Configuration Options

driver

The JDBC driver to use

url

The JDBC connection URL

user

The database user to connect as

password

The password to connect with

rdn

The LDAP atribute used as the rdn for all objects generated

mapping

Comma sepperated list of ldap=db mapping of attributes

objectClass

The objectClass for all octs created by this insert

sql

The SQL use to determine what objects this insert will generate. This SQL must include all attributes included in the mapping configuration

maxCons

The maximum number of connections, default is 5

maxIdleCon

The maximum amount of time a connection can remain idle before closing

useSimple

true or false, utilizes the "simple" SQL, mutch better performance but only the value of multivalued attributes in the filter are returned

We are going to integrate a simple database table that will contain the following columns

Column

Type

Description

id

integer

The unique id of the user

firstName

varchar(50)

The use’s first name

lastName

varchar(50)

The user’s last name

email

varchar(50)

The user’s email address

location

varchar(50)

The user’s home city

You will notice that there are three fields missing that are typically a included in any directory object: uid, cn and userPassword. Each of these fields have been left out because they generally do not exist in databases. Databases are rarely used for authentication so there is rarely a uid and password. Because databases tend to normalize data, they don’t store a full name in a single field. If this is a requirement, we will cover how to handle these cases later on.

The table we are going to work with is called "employees" with the following data:

id

firstname

lastname

email

location

1

John

Doe

jdoe@test.com

Boston

2

Jen

Burnstein

jburnstein@test.com

Boston

3

Josh

Bora

jbora@test.com

New York

4

Alice

Raden

araden@test.com

Los Angeles

The first step is to download MySQL and the MySQL jdbc drivers. Thisguide doesn’t cover setting up MySQL. Once you downloaded MySQL and have setup the database, copy mysql-connector-java-VERSION-bin.jar to the lib directory of your myvd installation. If you are using the MyVD startup script MyVD will automatically pickup the drivers. Once deployed we can create our configuration.

Once downloaded, the myvd.conf file needs to be configured to utilize the employees table. Below is the configuration we are going to use:

#Listen on port 389
server.listener.port=10389

#Listen on 636 using SSL
#server.secure.listener.port=636
#server.secure.keystore=/var/keystores/myvd.ks
#server.secure.keypass=secret

#Configure global chains
server.globalChain=LogAllTransactions
server.globalChain.LogAllTransactions.className=net.sourceforge.myvd.inserts.DumpTransaction
server.globalChain.LogAllTransactions.config.logLevel=info
server.globalChain.LogAllTransactions.config.label=Global


#Configure namespaces
server.nameSpaces=Root,DBEmployees

#Define RootDSE
server.Root.chain=RootDSE
server.Root.nameSpace=
server.Root.weight=0
server.Root.RootDSE.className=net.sourceforge.myvd.inserts.RootDSE
server.Root.RootDSE.config.namingContexts=ou=employees,o=mycompany,c=us

server.DBEmployees.chain=DB
server.DBEmployees.nameSpace=ou=employees,o=mycompany,c=us
server.DBEmployees.weight=0
server.DBEmployees.DB.className=net.sourceforge.myvd.inserts.jdbc.JdbcInsert
server.DBEmployees.DB.config.driver=com.mysql.jdbc.Driver
server.DBEmployees.DB.config.url=jdbc:mysql://localhost/myvdtest
server.DBEmployees.DB.config.user=root
server.DBEmployees.DB.config.password=
server.DBEmployees.DB.config.rdn=empid
server.DBEmployees.DB.config.mapping=empid=id,givenName=firstname,sn=lastname,mail=email,l=location
server.DBEmployees.DB.config.objectClass=employeePerson
server.DBEmployees.DB.config.sql=SELECT id,firstname,lastname,email,location FROM employees

This configuration has four major parts:

  1. LDAP Listen Configuration - Determines what port MyVD will listen on

  2. Global Server Configuration - Configures the global chanin, in this case the global chain only contains the LogAllTransactions insert

  3. Root DSE Configuration - Configures the root DSE, in this case the RootDSE insert is configured to generate naming contexts

  4. Database Configuration - The database configuration

The first three configuration points are a part of the standard sample myvd.conf file included in the download. The final configuration point is the heart of the matter. The driver, url, user and password configuration options general options. The rdn option is the LDAP name of the database field, in this case the empid LDAP attribute is mapped to the id database field. The mapping option is the ldap=db mapping. The left hand side of the equals is the LDAP name and the right side of the equals is the database field name. The objectClass configuration is what object class entries from this insert will have. This can be any object class. Finally the sql option is an SQL statement that provides all of the database fields. This can be a simple SELECT as in the above case or can be something more complex as long as every column listed in the mapping configuration is included.

Thats it. Thats all thats involved. Below are the more advanced topics involved in exposing a database via MyVD.

Composite Attributes

Unlike LDAP directories, relational databases do not allways store information in a final "view" state. The simplest such example of this is the common name, or "cn" attribute. Another such example is the "uid" attribute. In the below example, we are going to add to our simple DB insert to include both a uid an cn attribute.

When mapping attributes its important to understand how the mapping process works. There are two directions to every LDAP request. The first is the inbound request. On the inbound request the attributes requested and the filter used must be updated. For instance if you are requesting the "uid" attribute, which in our case is a compound attribute of the givenName and sn attributes, then the request must instead include a request for the givenName and sn attribute. If the filter for the request is "(uid=sperson)" then the filter must be transformed to "(&(givenName=S*)(sn=Person))". The outbound result then needs to construct the attribute from the components. Here’s an example. The below table shows the initial request and the final request:

Inital Form

Transformed

Base

ou=employees-uid_cn,o=mycompany,c=us

ou=employees-uid_cn,o=mycompany,c=us

Scope

Subtree

Subtree

Attributes

uid,cn,l

givenName,sn,l

Filter

(&(l=New York)(uid=jbora)))

(&(l=New York)(&(givenName=J*)(sn=Bora))))

MyVD includes an insert to perform all of this work for you called the composite attribute insert. This insert takes the attributes that are to be assembled and creates the attribute on outbound entries and adjusts all requests accordingly. Fist lets look at how teh composite attribute insert is configured:

Composite Attribute Insert

The composite attribute insert is designed to create a singe attribute from multiple attributes. Uses include creating a CN attribute from the givenName and sn as well as creating a uid from the same two attributes.

Class Name

net.sourceforge.myvd.inserts.jdbc.CompositeAttrib

Scope

Search

Configuration Options

attribute

The name of the attribute that will be created (ie uid or cn)

components

Comma seperated list of component attributes with a colon to determine how much of the attribute. The length of the compoenent can be a number, fs for the first space, ls for the last space and * for the entire attribute. ie "givenName:1,sn:*"

properCase

true or false. This configurate determines if the first letter of a component should be upper case on inbound transactions and lowercase in outbound transactions.

objectClass

The objectClass that this insert will apply to.

The "attribute" parameter is pretty straight forward. The components configuration is a bit more comlex. The goal of this configuration option is to determine how an attribute is created. The value for this option is "attribute:length,attribute:length,…". The length can be either a number, "" to indicate the entire attribute, "fs" to indicate that the attribute goes until the first space of the final attribute and finally "ls" for the last space of the final attribute. For instance a uid would be constructed by the first letter of the first name and the last name, or "givenName:1,sn:". A cn would be the entire first name, a space and then the last name or "givenName:fs,sn*". Some organizations include the middle initial in the first name, which means you would want the last space or "givenName:ls,sn:*". The next configuration, "properCase", is used to automaticlly set the case from all lowercase to a proper case. This is useful when you want your uid’s to be lowercase. Finally the objectClass configuration is used to determine when the composite attribute should be built.

Now that we’ve covered how the composite insert works and how its configured, lets look at the configuration:

#Listen on port 389
server.listener.port=10389

#Listen on 636 using SSL
#server.secure.listener.port=636
#server.secure.keystore=/var/keystores/myvd.ks
#server.secure.keypass=secret

#Configure global chains
server.globalChain=LogAllTransactions
server.globalChain.LogAllTransactions.className=net.sourceforge.myvd.inserts.DumpTransaction
server.globalChain.LogAllTransactions.config.logLevel=info
server.globalChain.LogAllTransactions.config.label=Global


#Configure namespaces
server.nameSpaces=Root,DBEmployees,DBEmployeesUidCn

#Define RootDSE
server.Root.chain=RootDSE
server.Root.nameSpace=
server.Root.weight=0
server.Root.RootDSE.className=net.sourceforge.myvd.inserts.RootDSE
server.Root.RootDSE.config.namingContexts=ou=employees-uid_cn,o=mycompany,c=us


server.DBEmployeesUidCn.chain=CleanAttribs,MakeUID,MakeCN,DB
server.DBEmployeesUidCn.nameSpace=ou=employees-uid_cn,o=mycompany,c=us
server.DBEmployeesUidCn.weight=0

server.DBEmployeesUidCn.CleanAttribs.className=net.sourceforge.myvd.inserts.mapping.AttributeCleaner

server.DBEmployeesUidCn.MakeUID.className=net.sourceforge.myvd.inserts.jdbc.CompositeAttrib
server.DBEmployeesUidCn.MakeUID.config.attribute=uid
server.DBEmployeesUidCn.MakeUID.config.components=givenname:1,sn:*
server.DBEmployeesUidCn.MakeUID.config.properCase=true
server.DBEmployeesUidCn.MakeUID.config.objectClass=employeePerson

server.DBEmployeesUidCn.MakeCN.className=net.sourceforge.myvd.inserts.jdbc.CompositeAttrib
server.DBEmployeesUidCn.MakeCN.config.attribute=cn
server.DBEmployeesUidCn.MakeCN.config.components=givenname:ls,sn:*
server.DBEmployeesUidCn.MakeCN.config.properCase=false
server.DBEmployeesUidCn.MakeCN.config.objectClass=employeePerson

server.DBEmployeesUidCn.DB.className=net.sourceforge.myvd.inserts.jdbc.JdbcInsert
server.DBEmployeesUidCn.DB.config.driver=com.mysql.jdbc.Driver
server.DBEmployeesUidCn.DB.config.url=jdbc:mysql://localhost/myvdtest
server.DBEmployeesUidCn.DB.config.user=root
server.DBEmployeesUidCn.DB.config.password=
server.DBEmployeesUidCn.DB.config.rdn=empid
server.DBEmployeesUidCn.DB.config.mapping=empid=id,givenName=firstname,sn=lastname,mail=email,l=location
server.DBEmployeesUidCn.DB.config.objectClass=employeePerson
server.DBEmployeesUidCn.DB.config.sql=SELECT id,firstname,lastname,email,location FROM employees

The above configuration is very similar to the original DB configuration. We have added three new inserts. The first insert ensures that the attributes returned on a search are the ones requested in the search. The second insert creates the UID attribute while the third insert creates a CN attributes.

Changing The RDN Attribute

Some applications require a particuler RDN attribute to work properly. This is usually true of applications that require the user id to be a part of the RDN. In our above example the "empid" attribute is used as the RDN. For our database this makes sence because the id is the most unique identifier for our users. However, we are integrating with an application requires the uid we constructed in the last tutorial to be our user’s RDN. In order to do this we must translate every inbound request and outbound response. There is an insert that does this work for us however called the SetRDN insert. Here is the SetRDN insert’s configuration:

SetRDN Insert

This insert allows for the RDN of an entry to be changed. For instance if the current RDN is the "CN" attribute this insert can change it to the "uid" attribute.

Class Name

net.sourceforge.myvd.inserts.setrdn.SetRDN

Scope

Search,Add,Modify,Delete,Rename,Bind

Configuration Options

internalRDN

The current RDN attribute name.

externalRDN

The RDN attribute that will be presented

The configuration is fairly straight forward. The internalRDN is the rdn stored in the directory while the externalRDN is what is presented to the user. The below configuration shows how the SetRDN insert can be added to our example:

#Listen on port 389
server.listener.port=10389

#Listen on 636 using SSL
#server.secure.listener.port=636
#server.secure.keystore=/var/keystores/myvd.ks
#server.secure.keypass=secret

#Configure global chains
server.globalChain=LogAllTransactions
server.globalChain.LogAllTransactions.className=net.sourceforge.myvd.inserts.DumpTransaction
server.globalChain.LogAllTransactions.config.logLevel=info
server.globalChain.LogAllTransactions.config.label=Global


#Configure namespaces
server.nameSpaces=Root,DBEmployees,DBEmployeesUidCn,DBEmployeesUidRDN

#Define RootDSE
server.Root.chain=RootDSE
server.Root.nameSpace=
server.Root.weight=0
server.Root.RootDSE.className=net.sourceforge.myvd.inserts.RootDSE
server.Root.RootDSE.config.namingContexts=ou=employees,o=mycompany,c=us|ou=employees-uid_cn,o=mycompany,c=us|ou=employees-uid_rdn,o=mycompany,c=us

,
,
,


server.DBEmployeesUidRDN.chain=CleanAttribs,SetRDN,MakeUID,MakeCN,DB
server.DBEmployeesUidRDN.nameSpace=ou=employees-uid_rdn,o=mycompany,c=us
server.DBEmployeesUidRDN.weight=0

server.DBEmployeesUidRDN.CleanAttribs.className=net.sourceforge.myvd.inserts.mapping.AttributeCleaner

server.DBEmployeesUidRDN.SetRDN.className=net.sourceforge.myvd.inserts.setrdn.SetRDN
server.DBEmployeesUidRDN.SetRDN.config.internalRDN=empid
server.DBEmployeesUidRDN.SetRDN.config.externalRDN=uid

server.DBEmployeesUidRDN.MakeUID.className=net.sourceforge.myvd.inserts.jdbc.CompositeAttrib
server.DBEmployeesUidRDN.MakeUID.config.attribute=uid
server.DBEmployeesUidRDN.MakeUID.config.components=givenname:1,sn:*
server.DBEmployeesUidRDN.MakeUID.config.properCase=true
server.DBEmployeesUidRDN.MakeUID.config.objectClass=employeePerson

server.DBEmployeesUidRDN.MakeCN.className=net.sourceforge.myvd.inserts.jdbc.CompositeAttrib
server.DBEmployeesUidRDN.MakeCN.config.attribute=cn
server.DBEmployeesUidRDN.MakeCN.config.components=givenname:ls,sn:*
server.DBEmployeesUidRDN.MakeCN.config.properCase=false
server.DBEmployeesUidRDN.MakeCN.config.objectClass=employeePerson

server.DBEmployeesUidRDN.DB.className=net.sourceforge.myvd.inserts.jdbc.JdbcInsert
server.DBEmployeesUidRDN.DB.config.driver=com.mysql.jdbc.Driver
server.DBEmployeesUidRDN.DB.config.url=jdbc:mysql://localhost/myvdtest
server.DBEmployeesUidRDN.DB.config.user=root
server.DBEmployeesUidRDN.DB.config.password=
server.DBEmployeesUidRDN.DB.config.rdn=empid
server.DBEmployeesUidRDN.DB.config.mapping=empid=id,givenName=firstname,sn=lastname,mail=email,l=location
server.DBEmployeesUidRDN.DB.config.objectClass=employeePerson
server.DBEmployeesUidRDN.DB.config.sql=SELECT id,firstname,lastname,email,location FROM employees

Thats it. We have now exposed our directory with a new RDN attribute.

Updating The Database

The JDBC insert is a read only insert. It is based on an SQL statement which can have almost anything in it, preventing any way of knowing how an update is processed. There are three main reasons why the JDBC insert does not manage updates to the database:

  1. Because the SQL can contain joins, composite attributes and functions making it impossible to know how data should be sent to the database.

  2. Modifications to multi-value attributes are difficult to know how to manage.

  3. Relational databases maintain the relationships between lookup tables, which LDAP has no knowledge of.

In order to update user entries MyVD allows you to create an insert that sits behind the JDBC insert that can be passed both the database-ldap mapping and a connection from the JDBC insert allowing the insert to to update the database. This insert can access three request variables:

MYVD_DB_CON_INSERT-NAME

The database connection from the connection pool. The "INSERT-NAME" is the name of the insert into the configuration.

MYVD_DB_LDAP2DB_INSERT-NAME

The ldap→db mapping, meaning that the key for this HashMap is the ldap attribute name with the value being the database name. The "INSERT-NAME" is the name of the insert into the configuration.

MYVD_DB_DB2LDAP_INSERT-NAME

The db→ldap mapping, meaning that the key for this HashMap is the db attribute name with the value being the ldap name. The "INSERT-NAME" is the name of the insert into the configuration.

This setup allows for MyVD to allow for database updates while not forcing you to manage database connections or mappings, re-using the configuration from the JDBC insert.

Updating a Single Table

MyVD has a simple JBDC table update insert that can be used for updating a single table. This insert makes it easy to update the database you have integrated without writing custom code. Here is the configuration for the DBTableUpdate insert:

Database Table Update

This insert contains the logic to update a single database table. It is configured BEHIND a database insert in order to utilize that insert’s connection pool and mapping.

Class Name

net.sourceforge.myvd.inserts.jdbc.DBTableUpdate

Scope

Add,Modify,Delete,Rename

Configuration Options

tableName

The name of the table to update in the database

dbInsertName

The name of the insert used by this insert

This insert utilizes the DB insert as described above by adding the DBTableUpdate insert AFTER the DB insert. The tableName is the name of the table being updated while the dbInsertName is the name of the insert on this chain whose database pool and mapping configuration will be used.

Configuring this insert is very easy. The below configuration adds this insert to our above example of creating a uid and setting the rdn:

.
.
.
server.DBEmployeesUidRDN.chain=CleanAttribs,SetRDN,MakeUID,MakeCN,DB,TableUpdate
.
.
.
server.DBEmployeesUidRDN.TableUpdate.className=net.sourceforge.myvd.inserts.jdbc.DBTableUpdate
server.DBEmployeesUidRDN.TableUpdate.config.tableName=employees
server.DBEmployeesUidRDN.TableUpdate.config.dbInsertName=DB

Now our example is updateable!

Authenticating Users

The JDBC Insert doesn’t provide any way to authenticate against the database. this is because there are no standards as far as databases are concerned on how to store passwords and authenticate users. Just as with performing writes to the database, MyVD can pass a custom insert the database connection and mapping information allowing the custom insert to perform the authentication in whatever manner is required by the database.

Using "Simple" SQL Mode

Databases and directories store data in very different ways. Databases store data in tables that "relate" to each other where as a directories store data as objects. This means that databases don’t store mutliple value for a field in a given row where a directory can. In order to handle this the jdbc insert utilizes a subquery to ensure that all attribute values are returned MyVD uses sub-selects to perform queries. The basic of the SQL is:

 SELECT fields FROM (SQL) WHERE rdnField IN (SELECT id FROM ... WHERE ...)

Depending on how your tables are indexed this can be very slow. If you aren’t able to index the proper fields or there are only single value attributes comming from the database you can use the "useSimple" flag on the jdbc insert to utilize a simpler SQL statement. This will improve performance but may sacrifice data consistency based on the type of filters being used.

Joining Identities

One of the main features and use cases of a virtual directory is to join identities in disparate stores. For instance, an enterprise directory may store enterprise wide user data, but a new application requires additional data (such as role based data). While the attributes could be added to the enterprise directory or an edge directory can be created and synchronized with the enterprise directory a simpler solution would be to setup a virtual directory to join, in real time, a database used to store the user data with data from the enterprise directory. The basic design is as follows:

  1. An OpenLDAP server that contains enterprise user data

  2. A MySQL database storing the userid, location and application attributes

  3. MyVD deployed joining the two sources

The enterprise data:

# users, domain.com
dn: ou=users,dc=domain,dc=com
ou: users
objectClass: organizationalUnit

# jsmith000, users, domain.com
dn: uid=jsmith000,ou=users,dc=domain,dc=com
userPassword:: c2VjcmV0
uid: jsmith000
sn: Smith
cn: Joe Smith
objectClass: inetOrgPerson

# jsmith002, users, domain.com
dn: uid=jsmith002,ou=users,dc=domain,dc=com
userPassword:: c2VjcmV0
uid: jsmith002
sn: Smith
cn: Jen Smith
objectClass: inetOrgPerson

# bdund003, users, domain.com
dn: uid=bdund003,ou=users,dc=domain,dc=com
userPassword:: c2VjcmV0
uid: bdund003
sn: Dund
cn: Briana Dund
objectClass: inetOrgPerson

# rsandburg015, users, domain.com
dn: uid=rsandburg015,ou=users,dc=domain,dc=com
userPassword:: c2VjcmV0
uid: rsandburg015
sn: Sandburg
cn: Robert Sandburg
objectClass: inetOrgPerson

The database table that will store the enterprise data :

Field

Type

Null

Key

Default

Extra

id

int(11)

NO

PRI

NULL

auto_increment

username

varchar(50)

YES

NULL

location

varchar(50)

YES

NULL

appAttrib1

varchar(50)

YES

NULL

appAttrib2

varchar(50)

YES

NULL

The data in the table:

username

location

appAttrib1

appAttrib2

jsmith000

Boston

widgets

doo-dads

jsmith002

Boston

wing-dings

doo-dads

bdund003

LA

wing-dings

thingamabobers

rsandburg015

Chicago

widgets

thingamabobers

Notice that "username" field in the database table and the uid LDAP attributes match. This will be how we join a record in MyVD using the joiner insert. Before we configure our joiner, first the database and the enterprise directory need to be configured in MyVD. Below is the MyVirtualDirectory configuration for the enterprise directory and the database.

#Listen on port 389
server.listener.port=10389

#Listen on 636 using SSL
#server.secure.listener.port=636
#server.secure.keystore=/var/keystores/myvd.ks
#server.secure.keypass=secret

#Configure global chains
server.globalChain=LogAllTransactions
server.globalChain.LogAllTransactions.className=net.sourceforge.myvd.inserts.DumpTransaction
server.globalChain.LogAllTransactions.config.logLevel=info
server.globalChain.LogAllTransactions.config.label=Global


#Configure namespaces
server.nameSpaces=Root,DBEmployees,LDAPEmployees

#Define RootDSE
server.Root.chain=RootDSE
server.Root.nameSpace=
server.Root.weight=0
server.Root.RootDSE.className=net.sourceforge.myvd.inserts.RootDSE
server.Root.RootDSE.config.namingContexts=o=db|o=ldap

server.DBEmployees.chain=DB
server.DBEmployees.nameSpace=o=db
server.DBEmployees.weight=0
server.DBEmployees.DB.className=net.sourceforge.myvd.inserts.jdbc.JdbcInsert
server.DBEmployees.DB.config.driver=com.mysql.jdbc.Driver
server.DBEmployees.DB.config.url=jdbc:mysql://127.0.0.1/myvdjoin
server.DBEmployees.DB.config.user=trenduser
server.DBEmployees.DB.config.password=secret
server.DBEmployees.DB.config.rdn=uid
server.DBEmployees.DB.config.mapping=uid=username,l=location,appAttrib1=appAttrib1,appAttrib2=appAttrib2
server.DBEmployees.DB.config.objectClass=dbPerson
server.DBEmployees.DB.config.sql=SELECT username,location,appAttrib1,appAttrib2 FROM userdata
server.DBEmployees.DB.config.useSimple=true

server.LDAPEmployees.chain=LDAP
server.LDAPEmployees.nameSpace=o=ldap
server.LDAPEmployees.weight=0
server.LDAPEmployees.LDAP.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.LDAPEmployees.LDAP.config.host=127.0.0.1
server.LDAPEmployees.LDAP.config.port=389
server.LDAPEmployees.LDAP.config.remoteBase=ou=users,dc=domain,dc=com

The above configuration is straight forward. A single ldap insert chain is created for the enterprise directory and a database insert chain for the application database table. Before configuring the joiner, lets go into the details of configuring the joiner:

The joiner is used to combine two LDAP namespaces (note that this means two DN namespace, not two configuration namespaces). The joiner combines entries from each namespace.

Class Name

net.sourceforge.myvd.inserts.join.Joiner

Scope

Search

Configuration Options

primaryNamespace

LDAP DN that represents a configured namespace that will drive the namespace of the joiner

joinedNamespace

LDAP DN that represents a configured namespace that will be joined with the primaryNamespace. The namespace of the primaryNamespace drives the joiner’s namespace

joinedAttributes

Attributes to be included in the join

joinFilter

A filter that is used to combine objects. When including an attribute from the joining entry prepend the attribute with "ATTR.". For instance, to combine a user based on the uid attribute the filter would be "(uid=ATTR.uid)"

bindPrimaryFirst

If set to true, MyVD will first try to bind the primary insert. If set to false it will try the joined insert

The joiner insert is designed to work only on searches. There is support for add, modify delete and rename operations with addtional inserts, which will be covered below. The joiner configuration is fairly straight forward. There are a few of concepts that need to be understood:

  1. Namepsace Joining - The joiner doesn’t join configuration namespaces (such as DBEmployees and LDAPEmployees in the above configuration) but instead joins directory namespaces (ie o=ldap, o=db). This means that you could join a single database to multiple directories with a single joiner if they were all in the same namepsace (ie join o=db with ou=people which may include a directory at ou=ineternal,ou=people and another directory at ou=external,ou=people).

  2. Primary Namspace - The primary namespace drives the structure of the directory. This means that if an entry exists in the primary namespace but not in the joined namespace the entry will still be returned. If the opposite is true, an entry exists in the joined namespace but not the primary namespace the entry won’t be returned to the client. For instance lets say the primary namespace is ou=people and the joined namespace is o=db the entry ou=internal,ou=people would be returned to the client but the entry ou=people,o=db would not (assuming the join filter is on the uid attribute).

  3. Joined Namespace - The joined namespace indicates which directory namspace to join the primary namspace too. Entries in the joined namespace are used only for adding additional attributes to entries in the primary namespace. The structure of the joined namespace is ignored.

  4. Join Filter - Entries between the two namespaces are joined based on a join filter. This filter is used to find the joined entry of an entry returned by a search. It is the same as a regular LDAP filter, but instead of specifying a value for an attribute, you can spcify ATTRIB.attribute to indicate that the value should be replaced for the join. For instance if joining with the filter "(&(uid=ATTRIB.uid)(|(objectClass=dbPerson)(objectClass=inetOrgPerson))" and the following entry were returned:

    # bdund003, ldap
    dn: uid=bdund003,o=ldap
    userPassword:: c2VjcmV0
    uid: bdund003
    sn: Dund
    cn: Briana Dund
    objectClass: inetOrgPerson

    The virtual directory server would then perform a search on o=db with the filter "(&(uid=bdund003)(|(objectClass=dbPerson)(objectClass=inetOrgPerson))", replacing the attribute ATTRIB.uid with the uid from the returned entry.

Now that the basic concepts of joining have been covered, lets configure a joiner. The below configuration joins our enterprise directory and application database:

#Listen on port 389
server.listener.port=10389

#Listen on 636 using SSL
#server.secure.listener.port=636
#server.secure.keystore=/var/keystores/myvd.ks
#server.secure.keypass=secret

#Configure global chains
server.globalChain=LogAllTransactions
server.globalChain.LogAllTransactions.className=net.sourceforge.myvd.inserts.DumpTransaction
server.globalChain.LogAllTransactions.config.logLevel=info
server.globalChain.LogAllTransactions.config.label=Global


#Configure namespaces
server.nameSpaces=Root,DBEmployees,LDAPEmployees,Joiner

#Define RootDSE
server.Root.chain=RootDSE
server.Root.nameSpace=
server.Root.weight=0
server.Root.RootDSE.className=net.sourceforge.myvd.inserts.RootDSE
server.Root.RootDSE.config.namingContexts=o=db|o=ldap|ou=people,dc=domain,dc=com

#Application specific database
server.DBEmployees.chain=DB
server.DBEmployees.nameSpace=o=db
server.DBEmployees.weight=0
server.DBEmployees.DB.className=net.sourceforge.myvd.inserts.jdbc.JdbcInsert
server.DBEmployees.DB.config.driver=com.mysql.jdbc.Driver
server.DBEmployees.DB.config.url=jdbc:mysql://127.0.0.1/myvdjoin
server.DBEmployees.DB.config.user=trenduser
server.DBEmployees.DB.config.password=secret
server.DBEmployees.DB.config.rdn=uid
server.DBEmployees.DB.config.mapping=uid=username,l=location,appAttrib1=appAttrib1,appAttrib2=appAttrib2
server.DBEmployees.DB.config.objectClass=dbPerson
server.DBEmployees.DB.config.sql=SELECT username,location,appAttrib1,appAttrib2 FROM userdata
server.DBEmployees.DB.config.useSimple=true

#Enterprise directory
server.LDAPEmployees.chain=LDAP
server.LDAPEmployees.nameSpace=o=ldap
server.LDAPEmployees.weight=0
server.LDAPEmployees.LDAP.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.LDAPEmployees.LDAP.config.host=127.0.0.1
server.LDAPEmployees.LDAP.config.port=389
server.LDAPEmployees.LDAP.config.remoteBase=ou=users,dc=domain,dc=com

#Join the database and directory on the UID attribute
server.Joiner.chain=joiner
server.Joiner.nameSpace=ou=people,dc=domain,dc=com
server.Joiner.weight=0
server.Joiner.joiner.className=net.sourceforge.myvd.inserts.join.Joiner
server.Joiner.joiner.config.primaryNamespace=o=ldap
server.Joiner.joiner.config.joinedNamespace=o=db
server.Joiner.joiner.config.joinedAttributes=uid,l,appattrib1,appattrib2
server.Joiner.joiner.config.joinFilter=(uid=ATTR.uid)

Now when we perform the search "(objectClass=*)" on the base "ou=people,dc=domain,dc=com" the following results are returned:

# people, domain.com
dn: ou=people,dc=domain,dc=com
ou: users
objectClass: organizationalUnit

# jsmith000, people, domain.com
dn: uid=jsmith000,ou=people,dc=domain,dc=com
primaryDN: uid=jsmith000,o=ldap
joinedDNs: uid=jsmith000,o=db
userPassword:: c2VjcmV0
uid: jsmith000
l: Boston
appattrib2: doo-dads
appattrib1: widgets
sn: Smith
cn: Joe Smith
objectClass: inetOrgPerson
objectClass: dbPerson

# jsmith002, people, domain.com
dn: uid=jsmith002,ou=people,dc=domain,dc=com
primaryDN: uid=jsmith002,o=ldap
joinedDNs: uid=jsmith002,o=db
userPassword:: c2VjcmV0
uid: jsmith002
l: Boston
appattrib2: doo-dads
appattrib1: wing-dings
sn: Smith
cn: Jen Smith
objectClass: inetOrgPerson
objectClass: dbPerson

# bdund003, people, domain.com
dn: uid=bdund003,ou=people,dc=domain,dc=com
primaryDN: uid=bdund003,o=ldap
joinedDNs: uid=bdund003,o=db
userPassword:: c2VjcmV0
uid: bdund003
l: LA
appattrib2: thingamabobers
appattrib1: wing-dings
sn: Dund
cn: Briana Dund
objectClass: inetOrgPerson
objectClass: dbPerson

# rsandburg015, people, domain.com
dn: uid=rsandburg015,ou=people,dc=domain,dc=com
primaryDN: uid=rsandburg015,o=ldap
joinedDNs: uid=rsandburg015,o=db
userPassword:: c2VjcmV0
uid: rsandburg015
l: Chicago
appattrib2: thingamabobers
appattrib1: widgets
sn: Sandburg
cn: Robert Sandburg
objectClass: inetOrgPerson
objectClass: dbPerson

Notice that all of the users have attributes from both the enterprise directory and the application specific database. In addion there are two additional attributes:

  1. primaryDN - The DN from the primary namespace

  2. joinedDNs - The DN(s) from the joined namepsaces

These attributes may be useful for tracking how a user is brought together.

Thats it! A simplistic, yet very useful, join has been created to support a specific application without adding attributes to the enterprise directory or creating a synchronized copy.

Updating, Adding and Deleting Joined Entries

As stated above the joiner is built to only directly support searches. This is because modifications may follow seperate rules and adds & deletes require knowing and understanding namespaces involved in a join. There are several two ways to perform writes to a joined namespace:

  1. If the joined namespace has a flat structure, a pre-built insert may be used

  2. If the joined naemspace has a deep structure a custom insert may be built

We will configure our virtual directory to be able to update both the enterprise and application specific directory. In order to support adds, modifies, deletes and renames we will configure several additional inserts:

  1. JoinAddFlatNS - Support the add and delete operations

    The join add flat namespace insert is designed to allow the addition of entries to a joiner namespace. NOTE: this insert MUST be configured as the last insert on a chain.

    Class Name

    net.sourceforge.myvd.inserts.join.JonAddFlatNS

    Scope

    Add

    Configuration Options

    joinerName

    The name of the joiner insert configured on this chain

    joinedObjectClass

    The objectclass for joined entries

    sharedAttributes

    Comma seperated list of attributes shared by both the primary and joined namespaces

  2. SimpleJoinModify - Supports the modification of joined entries

    The simple join modify insert is configured AFTER a joiner on a chain in order to support the modification of joined attributes

    Class Name

    net.sourceforge.myvd.inserts.join.SimpleJoinModify

    Scope

    Modify

    Configuration Options

    joinerName

    The name of the joiner insert configured on this chain

  3. DBTableUpdate - Supports the modification of the database table

    This insert contains the logic to update a single database table. It is configured BEHIND a database insert in order to utilize that insert’s connection pool and mapping.

    Class Name

    net.sourceforge.myvd.inserts.jdbc.DBTableUpdate

    Scope

    Add,Modify,Delete,Rename

    Configuration Options

    tableName

    The name of the table to update in the database

    dbInsertName

    The name of the insert used by this insert

The above inserts all have a few things in common. They are all configured AFTER their main insert and they relly on their main insert’s configuration. That is to say that the JoinAddFlatNS and SimpleJoinModify inserts are configured after the joiner insert and the DBTableUpdate insert is configured after the application database insert. In addition, they are configured to "know" about their main insert to eliminate double configurations. The below configuration will allow for the creation and modification of entries of the joiner we previously configured:

#Listen on port 389
server.listener.port=10389

#Listen on 636 using SSL
#server.secure.listener.port=636
#server.secure.keystore=/var/keystores/myvd.ks
#server.secure.keypass=secret

#Configure global chains
server.globalChain=LogAllTransactions
server.globalChain.LogAllTransactions.className=net.sourceforge.myvd.inserts.DumpTransaction
server.globalChain.LogAllTransactions.config.logLevel=info
server.globalChain.LogAllTransactions.config.label=Global


#Configure namespaces
server.nameSpaces=Root,DBEmployees,LDAPEmployees,Joiner

#Define RootDSE
server.Root.chain=RootDSE
server.Root.nameSpace=
server.Root.weight=0
server.Root.RootDSE.className=net.sourceforge.myvd.inserts.RootDSE
server.Root.RootDSE.config.namingContexts=o=db|o=ldap|ou=people,dc=domain,dc=com

server.DBEmployees.chain=DB,dbupdate
server.DBEmployees.nameSpace=o=db
server.DBEmployees.weight=0
server.DBEmployees.DB.className=net.sourceforge.myvd.inserts.jdbc.JdbcInsert
server.DBEmployees.DB.config.driver=com.mysql.jdbc.Driver
server.DBEmployees.DB.config.url=jdbc:mysql://127.0.0.1/myvdjoin
server.DBEmployees.DB.config.user=trenduser
server.DBEmployees.DB.config.password=secret
server.DBEmployees.DB.config.rdn=uid
server.DBEmployees.DB.config.mapping=uid=username,l=location,appAttrib1=appAttrib1,appAttrib2=appAttrib2
server.DBEmployees.DB.config.objectClass=dbPerson
server.DBEmployees.DB.config.sql=SELECT username,location,appAttrib1,appAttrib2 FROM userdata
server.DBEmployees.DB.config.useSimple=true
#The database update insert
server.DBEmployees.dbupdate.className=net.sourceforge.myvd.inserts.jdbc.DBTableUpdate
server.DBEmployees.dbupdate.config.tableName=userdata
server.DBEmployees.dbupdate.config.dbInsertName=DB


server.LDAPEmployees.chain=LDAP
server.LDAPEmployees.nameSpace=o=ldap
server.LDAPEmployees.weight=0
server.LDAPEmployees.LDAP.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.LDAPEmployees.LDAP.config.host=127.0.0.1
server.LDAPEmployees.LDAP.config.port=389
server.LDAPEmployees.LDAP.config.remoteBase=ou=users,dc=domain,dc=com
server.LDAPEmployees.LDAP.config.proxyDN=cn=admin,dc=domain,dc=com
server.LDAPEmployees.LDAP.config.proxyPass=secret

#The Joiner
server.Joiner.chain=joiner,joinmod,joinadd
server.Joiner.nameSpace=ou=people,dc=domain,dc=com
server.Joiner.weight=0
server.Joiner.joiner.className=net.sourceforge.myvd.inserts.join.Joiner
server.Joiner.joiner.config.primaryNamespace=o=ldap
server.Joiner.joiner.config.joinedNamespace=o=db
server.Joiner.joiner.config.joinedAttributes=uid,l,appattrib1,appattrib2
server.Joiner.joiner.config.joinFilter=(uid=ATTR.uid)

#The Join modification insert
server.Joiner.joinmod.className=net.sourceforge.myvd.inserts.join.SimpleJoinModify
server.Joiner.joinmod.config.joinerName=joiner

#The join add insert
server.Joiner.joinadd.className=net.sourceforge.myvd.inserts.join.JoinAddFlatNS
server.Joiner.joinadd.config.joinerName=joiner
server.Joiner.joinadd.config.joinedObjectClass=dbPerson
server.Joiner.joinadd.config.sharedAttributes=uid

Now you can create users and modify them seamlessly through your virtual directory.