Nested Mapping

Introduction

Penrose supports nested mapping, meaning that you can define a mapping under another mapping and it will inherit the values from the parent.

Example

Suppose we have a table "groups" which contains the information about a group including name and description. Then we have another table "usergroups" which contains the list of members of each groups in the form of <groupname, username> tuples. A group may contain 0 members. Then we also have a table "users" which contains the full user information. We want to create a mapping that produces the following output:

dn: cn=<group name>,ou=Groups,dc=Example,dc=com
objectClass: groupOfUniqueNames
cn: <group name>
description: <group description>

dn: uid=<member's username>,cn=<group name>,ou=Groups,dc=Example,dc=com
objectClass: person
uid: <member's username>
cn: <member's full name>
sn: <member's last name>

First we create the mapping for the groups, which is a direct mapping of the source fields into the attributes:

<entry dn="cn=...,ou=Groups,dc=Example,dc=com">

    <oc>groupOfUniqueNames</oc>
    <at name="cn" rdn="true">
        <variable>g.groupname</variable>
    </at>
    <at name="description">
        <variable>g.description</variable>
    </at>

    <source name="g">
        <source-name>groups</source-name>
        <field name="groupname">
            <expression>cn</expression>
        </field>
        <field name="description">
            <expression>description</expression>
        </field>
    </source>

</entry>

Then we define the mapping for the members. Note that the entries that appear under a particular group should be only the members of that group.

<entry dn="uid=...,cn=...,ou=Groups,dc=Example,dc=com">

    <oc>person</oc>
    <oc>inetOrgPerson</oc>
    <at name="uid" rdn="true">
        <variable>ug.username</variable>
    </at>
    <at name="cn">
        <expression>
if (u == void || u == null) return null;
return u.firstName+" "+u.lastName;
        </expression>
    </at>
    <at name="sn">
        <variable>u.lastName</variable>
    </at>

</entry>

The "uid" is mapped from usergroups' username field. The "cn" and "sn" is mapped from the users' first and last names.

Then in the same mapping we define the sources:

<entry dn="uid=...,cn=...,ou=Groups,dc=Example,dc=com">

    <source name="ug">
        <source-name>usergroups</source-name>
        <field name="groupname">
            <variable>g.groupname</variable>
        </field>
        <field name="username">
            <variable>uid</variable>
        </field>
    </source>

    <source name="u">
        <source-name>users</source-name>
        <field name="username">
            <variable>ug.username</variable>
        </field>
        <field name="firstName">
            <expression>
if (cn == void || cn == null) return null;
int i = cn.lastIndexOf(" ");
return cn.substring(0, i);
            </expression>
        </field>
        <field name="lastName">
            <expression>
if (cn == void || cn == null) return null;
int i = cn.lastIndexOf(" ");
return cn.substring(i+1);
            </expression>
        </field>
    </source>

</entry>

The "usergroups" source is linked to the "groups" source in the parent mapping by ug.groupname = g.groupname. This is to determine group membership. The "users" source is linked to the "usergroups" source by u.username = ug.username. This is to obtain the first and last name of the members.