Projects‎ > ‎

External Attribute Mapper


Overview

This project will provide a means to "map" external (attributes) to internal OpenPTK attributes.  This project is designed support by-directional attribute mapping.  There may be situations where a "Client" wants to submit a Request to the OpenPTK RESTful Web Service interface but the "Client" data (JSON/XML) is in a format that does not match a configured OpenPTK Context.  The "client" may be using a data format that is proprietary within its architecture.  Or, the "client" has formatted the data to support a de-facto or industry standard.

The Attribute mapping mechanism should extend the existing Context architecture.  The use of an "Attribute Mapper" should be dynamic and be applied on a per Operation / Transaction basis.

  The initial Use Case is to support external formats like:

Reference

Issue

Schemas

OpenPTK

The following table defines the "internal" Structure (payload) format used by each operation for both the inbound Request and outbound Response

 Operation Request Response
 Create
  •  subject
    • attributes
      • attribute
      • ...
 (none)
 Read (none)
  •  response
    • uri
    • state
    • status
    • subject
      • uniqueid
      • attributes
        • attribute
        • ...
 Update
  •  subject
    • attributes
      • attribute
      • ...
 (none)
 Delete (none) (none)
 Search (none)
  •  response
    • uri
    • state
    • length
    • offset
    • quantity
    • results
      • subject
        • uri
        • uniqueid
        • attributes
          • attribute
          • ...
      • ...

Design

Attribute Map Definition

Create a new XML Element within the openptk.xml configuration file.  These XML Elements will contain the 2-way mapping of external Subject / Attributes to the internal Subject / Attributes.  If no Schema is specified ... the attributes will be passed-through "as is". Thus, using the Context's declared Definition.

Processing

The solution should support both types of processing ... 
  • Inbound
    • The system is configured for inbound requests, to the Server ,from an external client.
  • Outbound
    • The system is configured for outbound requests, from the Server, to an external target system.

Integration: Inbound


The schema mapping can be integrated into the processing "flow" within the Resource class
  1. HTTP request
    1. obtain request data (JSON / XML)
    2. Resource class processes the HTTP Method
      1. For CREATE and UPDATE
        1. decode the data set
        2. create request Structure
        3. pre process the request Structure, if the attrmap properties are set
          1. converts external format to internal format for the Structure
      2. For READ and SEARCH
        1. process the request
        2. obtain response Structure
        3. post process the response Structure, if the attrmap properties are set
          1. converts internal format to external format for the Structure
    3. encode response using "external" response Structure
  2. send HTTP Response using encoded data

Implementation: Inbound

The "mapping" of an external structure to an internal structure is performed on a per-operation based.  A given request will "indicate" whether or not a "mapping" needs to be performed.  The Structure, containing the payload data, will need to set a property indicating which mapper to use.  

Structure Properties:

 Name: Description: Example:
 attrMap The identifier of a valid AttrMap, as define in the openptk.xml file scim-user-1.0-consumer

The Resource Servlet will look for an HTTP Header variable to determine if the Structure Property needs to set.

HTTP Header Variables:

 Name: Description: Example:
 X-OPENPTK-ATTRMAP The identifier of a valid Mapping, as define in the openptk.xml file SCIM-1.0-USER

Context validation:


Terminology

Mode

Declares whether a give Element should support none, one or both types of processing. Values:

 Value Description
 both supports INBOUND and OUTBOUND processing
 ignore skip for all modes of processing
 inbound support only INBOUND processing, data entering the system
 outbound support only OUTBOUND processing, data leaving the system


Attributes

Definitions from the SCIM specification:
  • Singular Attribute:
    • A Resource attribute that contains 0..1 values; e.g., displayName.
  • Multi-valued Attribute:
    • A Resource attribute that contains 0..n values; e.g., emails.
  • Simple Attribute:
    • A Singular or Multi-valued Attribute whose value is a primitive; e.g., String.
  • Complex Attribute:
    • A Singular or Multi-valued Attribute whose value is a composition of one or more Simple Attributes.
  • Sub-Attribute:
    • A Simple Attribute contained within a Complex Attribute.

The following Attributes are defined:

  • Singular Attribute: 
    • "name" : "..."
  • Complex Attribute: 
    • "name" : { "..." : "..." , "..." : "..." }
  • Multi-valued Attribute: 
    • "name" : [ "..." , "..." ]
    • "name" : [ { ... } , { ... } ]

Singular Attribute

The value is a String, so it is considered Simple Attribute
{
   "name":"value"
}
{
   "displayName" : "Barbara Jensen"
}

Complex Attribute

The value is a collection of one or more Simple Attributes
{
   "name" : {
      "name1" : "value1",
      "name2" : "value2"
   }
}

{
  "name": {
    "formatted": "Ms. Barbara J Jensen III",
    "familyName": "Jensen",
    "givenName": "Barbara",
    "middleName": "Jane",
    "honorificPrefix": "Ms.",
    "honorificSuffix": "III"
  }
}

Multi-valued Attribute (simple)

Each value is a String
{
   "name" : [ "value1", "value2" ]
}
{
  "emails": ["bjensen@example.com","babs@example.com"]
}

Multi-valued Attribute (complex)

Each "value" is a collection, of key-value pairs
{
   "name" : [
      {
         "name1" : "value1",
         "name2" : "value2",
         "name3" : "value3"
      },
      {
         "name1" : "value1",
         "name2" : "value2"
      }
   ]
}

{
  "emails": [
    {"value":"bjensen@example.com"},
    {"value":"babs@example.com"}
  ]
}

{
  "emails": [
    {
      "value": "bjensen@example.com",
      "type": "work",
      "primary": true
    },
    {
      "value": "babs@jensen.org",
      "type": "home"
    }
  ]
}

Data Types

The following types are supported
  • String
  • Boolean
  • Number
  • DateTime
  • Binary


Implementation

Package and Class Names

The following table outlines the naming plans for Java packages and classes

 Name Description
 org.openptk.config.mapper Package Name
 AttrMapIF Interface
 AttrMap Abstract base class, extends Attribute implements AttrMapIF
 BasicAttrMap Class, Default AttrMap, extends AttrMap
 Data Class for Datum Elements
 Datum Class, information for a multi-values Attribute
 ExternalAttr Class Extends Attribute class and implements ExternalAttrIF
 ExternalAttrIF Interface Extends AttributeIF interface
 ExternalMode Class, implements ExternalModeIF 
 ExternalModeIF Interface
 ExternalSubAttr Class, extends Attribute implements ExternalSubAttrIF
 ExternalSubAttrIF Interface
 Match Class
 Literal Class for Literal Elements, (similar to Functions)
 SubAttributes Class for Sub-Attribute Elements

Interface

public interface AttrMapIF
{
   public StructureIF externalToInternal(StructureIF external);
   public StructureIF internalToExternal(StructureIF internal);
}

Attribute Map Assignments

The design needs to include a mechanism to assign a given OpenPTK Context to a defined Attribute Map so that the Engine's Representation object will know how to process the "attributes", which will be provided as a generic Structure hierarchy.  Could leverage a "global" default attribute map ...


Context that wants to support the use of Attribute Mapping needs to set Assignments which must contain one or more Assignment elements. A given Assignment element contains a Source element and a Destination element.

A Context that contains an Assignment, does not mean that the attribute mapping will automatically be applied to each operation.  The mapping process will only apply to operations that meet the defined Source and related Destination assignment.  The mapping process is enabled when a Destination has a type of attrmap set to a valid value.  The Destination is only active when a operation supports a give Source rule.

The Assignment Source and Destination support the following types:

 Source Type Description Name Value
 http-headerReference an HTTP Header variable.

If the "named" header variable contains a "value" that matches the Source "value", then the condition is TRUE.

If a "value" is not set then the condition is always TRUE and the Source value is the value of the header variable.
The name of the Header variable.The value of the Header variable that is used to determine if the Destination should be enabled, thus activating the the mapping
 Destination Type Description Name Value
 attrmapReference a defined AttrMap element.

The named AttrMap will be applied if the Destination's condition evaluates to TRUE 
The name of a defined AttrMap n/a
 propertyReference an existing or new Property

The named Property will be updated (or created) using the Source's value.
The name of an existing or new Property n/a


These 
Assignments are used during the Server startup process.  If a Context sets an Assignment that uses a Destination "type" of "attrmap", each AttrMap will be checked with the Context to validate the configured mapping.  Validation will involve the comparison for AttrMap target attribute names with the attribute names in the Context's Definition.

The following sample shows how the openptk.xml file can be configured to map a HTTP Header variable to leverage a specific Attribute Map, based on the value of the variable:

<Context id="Employees-Embed-JDBC" ... >
...         
   <Assignments>
      <Assignment id="http-hdr-attrmap" description="HTTP Header for SCIM map">
         <Source      type="http-header" name="X-OPENPTK-ATTRMAP" value="SCIM-USER-1.0"/>
         <Destination type="attrmap"     name="scim.user-1.0-consumer"/>
      </Assignment>
      <Assignment id="http-hdr-alias-url" description="re-write URL for responses">
         <Source      type="http-header" name="X-OPENPTK-ALIAS-URL"/>
         <Destination type="property"    name="response.url"/>
      </Assignment>
   </Assignments>
...
</Context>

The above example defines how the OpenPTK Server should process a request that has been sent to the Employees-Embed-JDBC Context.
  • If the Source "type" is "http-header", process Header variables
  • If the HTTP Header contains a variable with a "name" of : X-OPENPTK-ATTRMAP
    • The HTTP Header variable will be read
  • If the Source "value" equals "SCIM-USER-1.0", (evaluation is TRUE) ...
  • If the Destination "type" is "attrmap", implement the mapping of attributes
  • Get the Destination "value"
    • validate the "value" as a correct attrmap

Representation of a Map


The Representation class contains the methods related to converting the Structure (generic object hierarchy), which represents the data to be processes, to the Input object for the Framework. The current design is based on a simple "pass through" model. The Structure contains an element called "attributes" which contains sub-Structures (containing names / values). The sub-Structures are converted "as is" into Attribute object which are then added to the Input object. Neither the "name" nor the "value" of an attribute is modified. It is assumed the that Structure contains sub-Structures which match the OpenPTK Context they are using.

Defaults

XML Element Arguments.  Many of the XML Elements support XML Arguments.  If an XML Argument is not explicitly set, a default value will be used.  The table below indicated what the default for many of the arguments.
 Name Description Element Default Value
 required Is the element is required Attribute false
 multivalued The element supports multiple values (Attribute with plural value) Attribute false
 type The data type of the value for the Element Attribute String
 mapto How the Elements "id" is mapped to an OpenPTK "id".  The default is the use the "id" as is. Attribute
 Value
 ${id}

Syntax

The XML Elements used to define how external data is "mapped" to OpenPTK internal Context compliant data.
  • AttrMaps
    • AttrMap
      • Attributes
        • Attribute
          • Mode
            • Literal
            • Data
              • Datum
                • Literal
              • Match
            • SubAttributes
              • SubAttribute
                • Literal
                • Data
                  • Datum
                    • Literal
                  • Match

Elements

AttrMaps / AttrMap

<AttrMaps>
   <AttrMap id="..." classname="...">
      ...
   </AttrMap>
   ...
</AttrMaps>

 Argument Required Default Description Example
 id yes Name of the AttrMap, must be unique for all Schemas(unique for all AttrMaps)
 classname yes Full package / classname of the Class that implements the AttrMapIF interfaceorg.openptk.config.attribute.BasicMap

Attributes / Attribute

<Attributes>
   <Attribute id="..." required="..." mapTo="..." multivalued="..." mode="..." type="...">
      ...
   </Attribute>
   ...
</Attributes>

 Argument Required Default Description Values
 id yes  Name of the attribute, must be unique for a given Schema (unique for the schema)
 required no false Declares if the attribute is required, or not "true" / "false"
 mapTo no ${id} The name of the internal OpenPTK attribute (unique within openptk.xml)
 multivalued no false The attribute supports multiple values (array of values)
 type no string Data type of the value(s) 

Mode

Defines if an attribute needs to be processes for either "inbound", "outbound", "both" or "neither" (via an "ignore")

<Mode id="...">
   <Literal ...>
   <Data>
      ...
   </Data>
   <SubAttributes ...>
      ...
   </SubAttributes>
</Mode>

 Argument Required Default Description Values
 id yes none Under which mode should sub-elements apply "inbound", "outbound", "both", "ignore"

Literal

Only used to override default processing: mode="both" and value "as is"
  • CAN NOT BE USED WITH DATA OR SUBATTRIBUTES
<Process value="..."/>

 Argument Required Default Description Values
 value yes none The value to be derived "${lastname}, ${firstname}"

Data / Datum / Match

A collection of Datum elements
  • Only used if the Attribute is "multivalued"
  • CAN NOT BE USED WITH SUB ATTRIBUTES OR PROCESSES 
<Data>
   <Datum id="..." mapTo="..." >
      ...
   </Datum>
   <Match id="..." datum="..." value="..." mapfrom="..." mapto="..."/>
   ...
</Data>

 Argument Required Default Description Values
 id yes  Name of the value, must be unique for a given Schema (unique for the schema)
 mapTo no ${id} The name of the internal OpenPTK attribute (unique within openptk.xml)

SubAttributes / SubAttribute

A Collection of Attribute elements
  • Only used if the Attribute supports sub-Attributes
  • CAN NOT BE USED WITH PROCESSES OR DATA
<Attributes>
   <Attribute id="..." required="..." mapTo="..." multivalued="..." type="...">
      <SubAttributes>
         <SubAttribute id="..." ...>
         </SubAttribute>
         ...
      </SubAttributes>
   </Attribute>
   ...
</Attributes>

Rules

The configuration shall support these processing rules:


Sample

Below is an example of AttrMap definitions

   <AttrMaps>
      <!-- SCIM 1.0 -->
      <AttrMap id="scim-user-1.0-resource" classname="org.openptk.config.mapper.ConsumerAttrMap">
         <Attributes>
            <Attribute id="id">
               <Mode id="ignore"/>
            </Attribute>
            <Attribute id="meta">
               <Mode id="ignore"/>
            </Attribute>
            <!-- SCIM Core Schema : Schemas -->
            <Attribute id="schemas" required="true" multivalued="true">
               <Mode id="outbound">
                  <Literal value="urn:scim:schemas:core:1.0"/>
               </Mode>
            </Attribute>
            <!-- SCIM User Schema : Singular Attributes -->
            <Attribute id="userName" required="true" mapto="uniqueId">
               <Mode id="both"/>
            </Attribute>
            <Attribute id="externalId">
               <Mode id="ignore"/>
            </Attribute>
            <Attribute id="name">
               <Mode id="inbound">
                  <SubAttributes>
                     <SubAttribute id="formatted" mode="ignore"/>
                     <SubAttribute id="familyName" mapto="lastname" required="true"/>
                     <SubAttribute id="givenName" mapto="firstname" required="true"/>
                     <SubAttribute id="middleName"/>
                     <SubAttribute id="honorificPrefix" mapto="title"/>
                     <SubAttribute id="honorificSuffix" mode="ignore"/>
                  </SubAttributes>
               </Mode>
               <Mode id="outbound">
                  <SubAttributes>
                     <SubAttribute id="formatted">
                        <Literal value="${firstname} ${lastname}"/>
                     </SubAttribute>
                     <SubAttribute id="familyName" mapto="lastname" required="true"/>
                     <SubAttribute id="givenName" mapto="firstname" required="true"/>
                     <SubAttribute id="middleName"/>
                     <SubAttribute id="honorificPrefix" mapto="title"/>
                  </SubAttributes>
               </Mode>
            </Attribute>
            <Attribute id="displayName">
               <Mode id="outbound">
                  <Literal value="${lastname}, ${firstname}"/>
               </Mode>
            </Attribute>
            <Attribute id="nickName">
               <Mode id="ignore"/>
            </Attribute>
            <Attribute id="userType">
               <Mode id="ignore"/>
            </Attribute>
            <Attribute id="preferredLanguage">
               <Mode id="ignore"/>
            </Attribute>
            <Attribute id="locale">
               <Mode id="ignore"/>
            </Attribute>
            <Attribute id="timezone">
               <Mode id="ignore"/>
            </Attribute>
            <Attribute id="active" type="boolean">
               <Mode id="ignore"/>
            </Attribute>
            <!-- SCIM User Schema : Plural Attributes -->
            <Attribute id="emails" multivalued="true">
               <Mode id="both">
                  <Data id="email" undefined="value">
                     <Datum id="type"/>
                     <Datum id="value"/>
                     <Datum id="primary" type="boolean"/>
                     <Match id="work_email" datum="type" value="work" mapto="email"/>
                  </Data>
               </Mode>
            </Attribute>
            <Attribute id="phoneNumbers" multivalued="true">
               <Mode id="both">
                  <Data id="phoneNumber" undefined="value">
                     <Datum id="type"/>
                     <Datum id="value"/>
                     <Datum id="primary" type="boolean"/>
                     <Match id="work_phone" datum="type" value="work" mapto="phone"/>
                  </Data>
               </Mode>
            </Attribute>
            <Attribute id="ims" multivalued="true">
               <Mode id="ignore"/>
            </Attribute>
            <Attribute id="photos" multivalued="true">
               <Mode id="ignore"/>
            </Attribute>
            <Attribute id="addresses" multivalued="true">
               <Mode id="both">
                  <Data id="address">
                     <Datum id="type"/>
                     <Datum id="primary" type="boolean"/>
                     <Datum id="formatted" mode="ignore"/>
                     <Datum id="streetAddress"/>
                     <Datum id="locality"/>
                     <Datum id="region"/>
                     <Datum id="postalCode"/>
                     <Datum id="country"/>
                     <Match id="work_address"    datum="type" value="work" mapfrom="streetAddress" mapto="address"/>
                     <Match id="work_city"       datum="type" value="work" mapfrom="locality" mapto="city"/>
                     <Match id="work_state"      datum="type" value="work" mapfrom="region" mapto="state"/>
                     <Match id="work_postalcode" datum="type" value="work" mapfrom="postalCode" mapto="postalcode"/>
                     <Match id="work_country"    datum="type" value="work" mapfrom="country" mapto="country"/>
                  </Data>
               </Mode>
            </Attribute>
            <Attribute id="groups" multivalued="true">
               <Mode id="ignore"/>
            </Attribute>
            <Attribute id="entitlements" multivalued="true">
               <Mode id="ignore"/>
            </Attribute>
            <Attribute id="roles" multivalued="true">
               <Mode id="ignore"/>
            </Attribute>
         </Attributes>
      </AttrMap>
</AttrMaps>

Sample curl command to trigger attribute mapping


Below is an example of AttrMap using the curl command

$ curl -v -c cookies.txt -H "Accept: text/plain" http://localhost:8080/openptk-server/login?user=openptkconfig\&password=password\&clientid=identitycentral

$ curl -X POST \
-v -b cookies.txt \
-H "Content-Type: application/json" \
-H "X-OPENPTK-ATTRMAP: scim-user-1.0-consumer" \
-d '{ "schemas":["urn:scim:schemas:core:1.0"],"id":"xyz123","userName":"bjensen@example.com","name":{"familyName":"Jensen","givenName":"Barbara" },"emails":[{"value":"bjensen@example.com","type":"work","primary":true}],"phoneNumbers":[{"value":"555-555-5555","type":"work"}],"userType":"Employee","title":"Tour Guide"}' http://localhost:8080/openptk-server/resources/contexts/Employees-Embed-JDBC/subjects