com.unboundid.scim.ldap.MultiValuedAttributeMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scim-ldap Show documentation
Show all versions of scim-ldap Show documentation
The UnboundID SCIM-LDAP module builds on the UnboundID SCIM-SDK to provide
classes that map SCIM resources to LDAP entries and vice versa. It also
contains several APIs that may be used to implement custom behaviors for
the mapping configuration file to extend its capabilities above and beyond
those provided out of the box. Each extension type varies in the amount of
control the implementation has over the mapping process and the amount of
effort required for implementation.
/*
* Copyright 2011-2012 UnboundID Corp.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (GPLv2 only)
* or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
package com.unboundid.scim.ldap;
import com.unboundid.asn1.ASN1OctetString;
import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.controls.ServerSideSortRequestControl;
import com.unboundid.ldap.sdk.controls.SortKey;
import com.unboundid.scim.schema.AttributeDescriptor;
import com.unboundid.scim.sdk.InvalidResourceException;
import com.unboundid.scim.sdk.SCIMAttribute;
import com.unboundid.scim.sdk.SCIMAttributeValue;
import com.unboundid.scim.sdk.SCIMObject;
import com.unboundid.scim.sdk.SCIMFilter;
import com.unboundid.scim.sdk.SCIMFilterType;
import com.unboundid.scim.sdk.SimpleValue;
import com.unboundid.scim.sdk.SortParameters;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This class provides an attribute mapper for multi-valued attributes.
* Each type of value (e.g. "work", "home") is mapped to one or more
* single-valued LDAP attributes, where each LDAP attribute holds one
* of the SCIM sub-attributes.
*/
public class MultiValuedAttributeMapper extends AttributeMapper
{
/**
* A set of value mappers for each value of the "type" sub-attribute.
*/
private final Map valueMappers;
/**
* The set of LDAP attributes that are mapped by this attribute mapper.
*/
private final Set ldapAttributeTypes;
/**
* Create a new instance of a complex attribute mapper.
*
* @param attributeDescriptor The SCIM attribute type that is mapped by this
* attribute mapper.
* @param canonicalValueMappers The set of complex value mappers for this
* attribute mapper.
*/
public MultiValuedAttributeMapper(
final AttributeDescriptor attributeDescriptor,
final Collection canonicalValueMappers)
{
super(attributeDescriptor);
ldapAttributeTypes = new HashSet();
valueMappers = new HashMap();
for (final CanonicalValueMapper canonicalValueMapper :
canonicalValueMappers)
{
valueMappers.put(canonicalValueMapper.getTypeValue(),
canonicalValueMapper);
for (final SubAttributeTransformation sat :
canonicalValueMapper.getTransformations())
{
ldapAttributeTypes.add(
sat.getAttributeTransformation().getLdapAttribute());
}
}
}
@Override
public Set getLDAPAttributeTypes()
{
return ldapAttributeTypes;
}
@Override
public Filter toLDAPFilter(final SCIMFilter filter)
throws InvalidResourceException
{
final SCIMFilterType type = filter.getFilterType();
List ldapFilterTypes = null;
List ldapFilterValues = null;
if(type != SCIMFilterType.AND && type != SCIMFilterType.OR)
{
String subAttributeName =
filter.getFilterAttribute().getSubAttributeName();
if (subAttributeName == null)
{
subAttributeName = "value";
}
final List selectedTransformations =
new ArrayList();
for (final CanonicalValueMapper canonicalValueMapper :
valueMappers.values())
{
for (final SubAttributeTransformation sat :
canonicalValueMapper.getTransformations())
{
if (sat.getSubAttribute().equalsIgnoreCase(subAttributeName))
{
selectedTransformations.add(sat);
}
}
}
if (selectedTransformations.isEmpty())
{
getAttributeDescriptor().getSubAttribute(subAttributeName);
return null; //match nothing
}
ldapFilterTypes = new ArrayList(selectedTransformations.size());
ldapFilterValues = new ArrayList(selectedTransformations.size());
for (final SubAttributeTransformation sat : selectedTransformations)
{
final AttributeTransformation at = sat.getAttributeTransformation();
final String filterValue;
if (filter.getFilterValue() != null)
{
filterValue = at.getTransformation().toLDAPFilterValue(
filter.getFilterValue());
}
else
{
filterValue = null;
}
ldapFilterTypes.add(at.getLdapAttribute());
ldapFilterValues.add(filterValue);
}
}
else
{
// We don't have to worry about AND and OR filter types since they are
// handled earlier by the resource mapper.
throw new RuntimeException("Invalid filter type: " + type);
}
final List filterComponents =
new ArrayList(ldapFilterTypes.size());
for (int i = 0; i < ldapFilterTypes.size(); i++)
{
final String ldapAttributeType = ldapFilterTypes.get(i);
final String ldapFilterValue = ldapFilterValues.get(i);
switch (type)
{
case EQUALITY:
{
filterComponents.add(
Filter.createEqualityFilter(ldapAttributeType,
ldapFilterValue));
}
break;
case CONTAINS:
{
filterComponents.add(
Filter.createSubstringFilter(ldapAttributeType,
null,
new String[] { ldapFilterValue },
null));
}
break;
case STARTS_WITH:
{
filterComponents.add(
Filter.createSubstringFilter(ldapAttributeType,
ldapFilterValue,
null,
null));
}
break;
case PRESENCE:
{
filterComponents.add(Filter.createPresenceFilter(ldapAttributeType));
}
break;
case GREATER_THAN:
case GREATER_OR_EQUAL:
{
return Filter.createGreaterOrEqualFilter(ldapAttributeType,
ldapFilterValue);
}
case LESS_THAN:
case LESS_OR_EQUAL:
{
return Filter.createLessOrEqualFilter(ldapAttributeType,
ldapFilterValue);
}
default:
throw new RuntimeException(
"Filter type " + type + " is not supported");
}
}
if (filterComponents.size() == 1)
{
return filterComponents.get(0);
}
else
{
return Filter.createORFilter(filterComponents);
}
}
@Override
public ServerSideSortRequestControl toLDAPSortControl(
final SortParameters sortParameters)
throws InvalidResourceException
{
String subAttributeName = sortParameters.getSortBy().getSubAttributeName();
if (subAttributeName == null)
{
subAttributeName = "value";
}
SubAttributeTransformation selectedTransformation = null;
for (final CanonicalValueMapper canonicalValueMapper :
valueMappers.values())
{
for (final SubAttributeTransformation sat :
canonicalValueMapper.getTransformations())
{
if (sat.getSubAttribute().equalsIgnoreCase(subAttributeName))
{
selectedTransformation = sat;
break;
}
}
if(selectedTransformation != null)
{
break;
}
}
if (selectedTransformation == null)
{
// Make sure the sub-attribute is defined
getAttributeDescriptor().getSubAttribute(subAttributeName);
return null; //sort by nothing
}
final AttributeTransformation attributeTransformation =
selectedTransformation.getAttributeTransformation();
if(attributeTransformation.getTransformation()
instanceof DefaultTransformation)
{
final AttributeDescriptor subAttributeDescriptor =
getAttributeDescriptor().getSubAttribute(subAttributeName);
final boolean reverseOrder = !sortParameters.isAscendingOrder();
if(subAttributeDescriptor.getDataType() ==
AttributeDescriptor.DataType.STRING)
{
return new ServerSideSortRequestControl(
new SortKey(attributeTransformation.getLdapAttribute(),
subAttributeDescriptor.isCaseExact() ?
CASE_EXACT_OMR_OID : CASE_IGNORE_OMR_OID, reverseOrder));
}
return new ServerSideSortRequestControl(
new SortKey(attributeTransformation.getLdapAttribute(),
reverseOrder));
}
else
{
throw new InvalidResourceException("Cannot sort by attribute " +
sortParameters.getSortBy() + " because it is mapped with custom " +
"transformations");
}
}
@Override
public void toLDAPAttributes(final SCIMObject scimObject,
final Collection attributes)
throws InvalidResourceException {
final SCIMAttribute scimAttribute =
scimObject.getAttribute(getAttributeDescriptor().getSchema(),
getAttributeDescriptor().getName());
if (scimAttribute != null)
{
for (SCIMAttributeValue v : scimAttribute.getValues())
{
final SCIMAttribute typeAttr = v.getAttribute("type");
CanonicalValueMapper canonicalValueMapper = null;
if (typeAttr != null)
{
final String type = typeAttr.getValue().getStringValue();
canonicalValueMapper = valueMappers.get(type);
}
// Check for a default mapper.
if (canonicalValueMapper == null)
{
canonicalValueMapper = valueMappers.get(null);
}
if (canonicalValueMapper != null)
{
for (final SubAttributeTransformation sat :
canonicalValueMapper.getTransformations())
{
final AttributeTransformation at = sat.getAttributeTransformation();
final String scimType = sat.getSubAttribute();
final String ldapType = at.getLdapAttribute();
final SCIMAttribute subAttribute = v.getAttribute(scimType);
if (subAttribute != null)
{
final AttributeDescriptor subDescriptor =
getAttributeDescriptor().getSubAttribute(scimType);
final ASN1OctetString value = at.getTransformation().toLDAPValue(
subDescriptor, subAttribute.getValue().getValue());
attributes.add(new Attribute(ldapType, value));
}
}
}
}
}
}
@Override
public SCIMAttribute toSCIMAttribute(final Entry entry)
throws InvalidResourceException {
final List values = new ArrayList();
for (final CanonicalValueMapper canonicalValueMapper :
valueMappers.values())
{
if (canonicalValueMapper.getTypeValue() != null)
{
final List subAttributes =
new ArrayList();
for (final SubAttributeTransformation sat :
canonicalValueMapper.getTransformations())
{
final AttributeTransformation at = sat.getAttributeTransformation();
final String scimType = sat.getSubAttribute();
final String ldapType = at.getLdapAttribute();
final AttributeDescriptor subDescriptor =
getAttributeDescriptor().getSubAttribute(scimType);
final Attribute a = entry.getAttribute(ldapType);
if (a != null)
{
final ASN1OctetString[] rawValues = a.getRawValues();
if (rawValues.length > 0)
{
final SimpleValue simpleValue =
at.getTransformation().toSCIMValue(subDescriptor,
rawValues[0]);
subAttributes.add(
SCIMAttribute.create(
subDescriptor, new SCIMAttributeValue(simpleValue)));
}
}
}
if (!subAttributes.isEmpty())
{
if (canonicalValueMapper.getTypeValue() != null)
{
subAttributes.add(SCIMAttribute.create(
getAttributeDescriptor().getSubAttribute("type"),
SCIMAttributeValue.createStringValue(
canonicalValueMapper.getTypeValue())));
}
final SCIMAttributeValue complexValue =
SCIMAttributeValue.createComplexValue(subAttributes);
values.add(complexValue);
}
}
else
{
if (canonicalValueMapper.getTransformations().size() > 0)
{
final SubAttributeTransformation sat =
canonicalValueMapper.getTransformations().iterator().next();
final AttributeTransformation at = sat.getAttributeTransformation();
final String scimType = sat.getSubAttribute();
final String ldapType = at.getLdapAttribute();
final Attribute a = entry.getAttribute(ldapType);
if (a != null)
{
for (final ASN1OctetString v : a.getRawValues())
{
final List subAttributes =
new ArrayList();
final AttributeDescriptor subDescriptor =
getAttributeDescriptor().getSubAttribute(scimType);
final SimpleValue simpleValue =
at.getTransformation().toSCIMValue(subDescriptor, v);
final SCIMAttributeValue scimValue =
new SCIMAttributeValue(simpleValue);
subAttributes.add(SCIMAttribute.create(
subDescriptor, scimValue));
final SCIMAttributeValue complexValue =
SCIMAttributeValue.createComplexValue(subAttributes);
values.add(complexValue);
}
}
}
}
}
if (values.isEmpty())
{
return null;
}
else
{
return SCIMAttribute.create(
getAttributeDescriptor(),
values.toArray(new SCIMAttributeValue[values.size()]));
}
}
}