com.unboundid.scim.ldap.MembersDerivedAttribute Maven / Gradle / Ivy
Show all versions of scim-ldap Show documentation
/*
* 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.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPURL;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchResultEntry;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.scim.schema.AttributeDescriptor;
import com.unboundid.scim.sdk.Debug;
import com.unboundid.scim.sdk.DebugType;
import com.unboundid.scim.sdk.InvalidResourceException;
import com.unboundid.scim.sdk.ResourceNotFoundException;
import com.unboundid.scim.sdk.SCIMAttribute;
import com.unboundid.scim.sdk.SCIMAttributeValue;
import com.unboundid.scim.sdk.SCIMException;
import com.unboundid.scim.sdk.SCIMObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
/**
* This class provides a derived attribute implementation for the members
* attribute in Group resources. For static groups, the members are taken from
* the member or uniqueMember attributes in the group entry. For dynamic groups,
* the members are derived by searching the DIT for user entries containing the
* group DN as a value of the isMemberOf attribute.
*
* The <derivation> element for this derived attribute accepts a special
* child element, <LDAPSearchRef idref="exampleSearchParams"/>, which
* specifies the LDAP search parameters to use when searching for group members.
*/
public class MembersDerivedAttribute extends DerivedAttribute
{
/**
* The name of the LDAP member attribute.
*/
private static final String ATTR_MEMBER = "member";
/**
* The name of the LDAP objectClass attribute.
*/
private static final String ATTR_OBJECT_CLASS = "objectClass";
/**
* The name of the LDAP uniqueMember attribute.
*/
private static final String ATTR_UNIQUE_MEMBER = "uniqueMember";
/**
* The name of the LDAP memberUrl attribute.
*/
private static final String ATTR_MEMBER_URL = "memberURL";
/**
* The name of the LDAP groupOfNames object class.
*/
private static final String OC_GROUP_OF_NAMES = "groupOfNames";
/**
* The name of the LDAP groupOfUniqueNames object class.
*/
private static final String OC_GROUP_OF_UNIQUE_NAMES = "groupOfUniqueNames";
/**
* The name of the LDAP groupOfEntries object class.
*/
private static final String OC_GROUP_OF_ENTRIES = "groupOfEntries";
/**
* The name of the groupOfURLs object class.
*/
private static final String OC_GROUP_OF_URLS = "groupOfURLs";
/**
* The attribute descriptor for the derived attribute.
*/
private AttributeDescriptor descriptor;
/**
* The LDAPSearchResolver to use for user resource when looking for
* members which are part of this attribute.
*/
private LDAPSearchResolver userResolver;
/**
* The set of LDAP attribute types needed in the group entry.
*/
private static Set ldapAttributeTypes = new HashSet();
static
{
ldapAttributeTypes.add(ATTR_MEMBER);
ldapAttributeTypes.add(ATTR_UNIQUE_MEMBER);
ldapAttributeTypes.add(ATTR_OBJECT_CLASS);
ldapAttributeTypes.add(ATTR_MEMBER_URL);
}
@Override
public Set getLDAPAttributeTypes()
{
return ldapAttributeTypes;
}
@Override
public SCIMAttribute toSCIMAttribute(
final Entry entry,
final LDAPRequestInterface ldapInterface,
final LDAPSearchResolver groupResolver)
throws SCIMException
{
final List values = new ArrayList();
try
{
final Set attrSet = groupResolver.getFilterAndIdAttributes();
if(userResolver != null)
{
attrSet.addAll(userResolver.getFilterAndIdAttributes());
}
final String[] attrsToGet = attrSet.toArray(new String[attrSet.size()]);
String[] members = null;
if (entry.hasObjectClass(OC_GROUP_OF_NAMES) ||
entry.hasObjectClass(OC_GROUP_OF_ENTRIES))
{
if (entry.hasAttribute(ATTR_MEMBER))
{
members = entry.getAttributeValues(ATTR_MEMBER);
}
}
else if (entry.hasObjectClass(OC_GROUP_OF_UNIQUE_NAMES))
{
if (entry.hasAttribute(ATTR_UNIQUE_MEMBER))
{
members = entry.getAttributeValues(ATTR_UNIQUE_MEMBER);
}
}
else if (entry.hasObjectClass(OC_GROUP_OF_URLS))
{
// Determine the dynamic group members, their 'type' and their
// resource ID.
if(entry.hasAttribute(ATTR_MEMBER_URL))
{
final String[] memberURLs = entry.getAttributeValues(ATTR_MEMBER_URL);
for(String url : memberURLs)
{
final LDAPURL ldapURL = new LDAPURL(url);
final SearchRequest searchRequest =
new SearchRequest(ldapURL.getBaseDN().toString(),
SearchScope.SUB, ldapURL.getFilter(),
attrsToGet);
SearchResult searchResult = ldapInterface.search(searchRequest);
if (searchResult.getEntryCount() > 0)
{
for(SearchResultEntry rEntry : searchResult.getSearchEntries())
{
values.add(createMemberValue(groupResolver, rEntry));
}
}
}
}
}
// Determine the 'type' and resource ID for static and virtual static
// groups.
if (members != null)
{
for (final String memberDN : members)
{
if ((userResolver != null &&
userResolver.isDnInScope(memberDN)) ||
groupResolver.isDnInScope(memberDN))
{
final SearchRequest searchRequest =
new SearchRequest(memberDN, SearchScope.BASE,
"(objectclass=*)", attrsToGet);
SearchResult searchResult = ldapInterface.search(searchRequest);
if(searchResult.getEntryCount() == 1)
{
final SearchResultEntry rEntry =
searchResult.getSearchEntries().get(0);
values.add(createMemberValue(groupResolver, rEntry));
}
}
}
}
}
catch (LDAPException e)
{
Debug.debugException(e);
throw ResourceMapper.toSCIMException(
"Error searching for values of the members attribute: " +
e.getMessage(), e);
}
if (values.isEmpty())
{
return null;
}
else
{
return SCIMAttribute.create(getAttributeDescriptor(),
values.toArray(new SCIMAttributeValue[values.size()]));
}
}
@Override
public void initialize(final AttributeDescriptor descriptor)
{
this.descriptor = descriptor;
if(getArguments().containsKey(LDAP_SEARCH_REF))
{
Object o = getArguments().get(LDAP_SEARCH_REF);
if(o instanceof LDAPSearchResolver)
{
userResolver = (LDAPSearchResolver) o;
}
}
}
@Override
public AttributeDescriptor getAttributeDescriptor()
{
return descriptor;
}
/**
* Create a SCIM value for a group member.
*
* @param groupResolver The group resolver.
* @param entry The member entry.
*
* @return The member value created, or {@code null} if the member entry
* is not a SCIM group or user resource.
*
* @throws SCIMException If the attribute descriptor for the derived
* attribute is missing a sub-attribute.
*/
private SCIMAttributeValue createMemberValue(
final LDAPSearchResolver groupResolver,
final Entry entry)
throws SCIMException
{
List subAttributes = new ArrayList(2);
if (userResolver != null)
{
if(userResolver.isResourceEntry(entry))
{
final String resourceID =
userResolver.getIdFromEntry(entry);
subAttributes.add(SCIMAttribute.create(
getAttributeDescriptor().getSubAttribute("type"),
SCIMAttributeValue.createStringValue("User")));
subAttributes.add(SCIMAttribute.create(
getAttributeDescriptor().getSubAttribute("value"),
SCIMAttributeValue.createStringValue(resourceID)));
return SCIMAttributeValue.createComplexValue(subAttributes);
}
}
if(groupResolver.isResourceEntry(entry))
{
final String resourceID =
groupResolver.getIdFromEntry(entry);
subAttributes.add(SCIMAttribute.create(
getAttributeDescriptor().getSubAttribute("type"),
SCIMAttributeValue.createStringValue("Group")));
subAttributes.add(SCIMAttribute.create(
getAttributeDescriptor().getSubAttribute("value"),
SCIMAttributeValue.createStringValue(resourceID)));
return SCIMAttributeValue.createComplexValue(subAttributes);
}
//This group member is not within any SCIM scope
if(Debug.debugEnabled())
{
Debug.debug(Level.INFO, DebugType.OTHER,
"Skipping group member '" + entry.getDN() +
"' for group '" + entry.getDN() +
"' because it is not within the scope of the " +
"SCIM User or Group resources.");
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public void toLDAPAttributes(final SCIMObject scimObject,
final Collection attributes,
final LDAPRequestInterface ldapInterface,
final LDAPSearchResolver groupResolver)
throws InvalidResourceException
{
final SCIMAttribute scimAttribute =
scimObject.getAttribute(getAttributeDescriptor().getSchema(),
getAttributeDescriptor().getName());
if (scimAttribute != null)
{
for (SCIMAttributeValue v : scimAttribute.getValues())
{
String type = null;
final SCIMAttribute typeAttr = v.getAttribute("type");
if (typeAttr != null)
{
type = typeAttr.getValue().getStringValue();
}
final String resourceID =
v.getAttribute("value").getValue().getStringValue();
// Determine the DN for this member.
try
{
String dn = null;
if (type == null)
{
if (userResolver != null)
{
try
{
dn = userResolver.getDnFromId(ldapInterface, resourceID);
}
catch (ResourceNotFoundException e)
{
// That's OK. It might be a group.
}
}
if (dn == null)
{
dn = groupResolver.getDnFromId(ldapInterface, resourceID);
}
}
else if (type.equalsIgnoreCase("User"))
{
dn = userResolver.getDnFromId(ldapInterface, resourceID);
}
else if (type.equalsIgnoreCase("Group"))
{
dn = groupResolver.getDnFromId(ldapInterface, resourceID);
}
else
{
throw new InvalidResourceException(
"Group member type '" + type + " is not valid. Member values " +
"must be of type 'User' or 'Group'");
}
attributes.add(new Attribute(ATTR_UNIQUE_MEMBER, dn));
}
catch (Exception e)
{
Debug.debugException(e);
throw new InvalidResourceException(e.getMessage());
}
}
}
}
}