Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.unboundid.scim.ldap.MembersDerivedAttribute Maven / Gradle / Ivy
/*
* Copyright 2011-2019 Ping Identity Corporation
*
* 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.DN;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPSearchException;
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.AttributePath;
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.SCIMFilter;
import com.unboundid.scim.sdk.SCIMFilterType;
import com.unboundid.scim.sdk.SCIMObject;
import com.unboundid.util.StaticUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
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
{
/**
* An enum for the different possible values of the joinAttribute entry in the
* scim-resources.xml file.
*/
public enum LDAPJoinAttribute {
/**
* Indicates the joinAttribute is set to member.
*/
MEMBER,
/**
* Indicates the joinAttribute is set to uniqueMember.
*/
UNIQUE_MEMBER,
/**
* Indicates the joinAttribute is set to memberURL.
*/
MEMBER_URL
}
/**
* The name of the argument that indicates whether to cache member data
* during an HTTP request, and how much data to cache. Values less than one
* will prevent member caching.
*/
private static final String MAX_MEMBERS_CACHED = "maxMembersCached";
/**
* The per-request member caches.
*/
private static final ThreadLocal> MEMBER_CACHES =
new ThreadLocal>();
/**
* The name of the LDAP member attribute.
*/
public static final String ATTR_MEMBER = "member";
/**
* The name of the LDAP uniqueMember attribute.
*/
public static final String ATTR_UNIQUE_MEMBER = "uniqueMember";
/**
* The name of the LDAP memberUrl attribute.
*/
public static final String ATTR_MEMBER_URL = "memberURL";
/**
* The name of the LDAP objectclass attribute.
*/
public static final String ATTR_OBJECTCLASS = "objectclass";
/**
* The name of the LDAP groupOfNames object class.
*/
public static final String OC_GROUP_OF_NAMES = "groupOfNames";
/**
* The name of the LDAP groupOfUniqueNames object class.
*/
public static final String OC_GROUP_OF_UNIQUE_NAMES = "groupOfUniqueNames";
/**
* The name of the LDAP groupOfEntries object class.
*/
public static final String OC_GROUP_OF_ENTRIES = "groupOfEntries";
/**
* The name of the groupOfURLs object class.
*/
public static final String OC_GROUP_OF_URLS = "groupOfURLs";
/**
* The attribute descriptor for the derived attribute.
*/
protected AttributeDescriptor descriptor;
/**
* The LDAPSearchResolver to use for user resource when looking for
* members which are part of this attribute.
*/
protected LDAPSearchResolver userResolver;
/**
* Indicates how many members to cache per request.
*/
private int membersToCachePerRequest;
/**
* Indicates if the join attribute is a member, uniqueMember, or memberURL.
*/
private LDAPJoinAttribute joinAttribute;
/**
* The set of LDAP attribute types needed in the group entry.
*/
protected static Set ldapAttributeTypes = new HashSet();
static
{
ldapAttributeTypes.add(ATTR_MEMBER);
ldapAttributeTypes.add(ATTR_UNIQUE_MEMBER);
ldapAttributeTypes.add(ATTR_MEMBER_URL);
}
/**
* A presence filter for objectclass.
*/
private static final Filter OBJECTCLASS_PRESENCE_FILTER =
Filter.createPresenceFilter(ATTR_OBJECTCLASS);
@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);
final SearchResult searchResult;
try
{
searchResult = ldapInterface.search(searchRequest);
}
catch (final LDAPSearchException lse)
{
Debug.debugException(lse);
continue;
}
if (searchResult.getEntryCount() > 0)
{
for(SearchResultEntry rEntry : searchResult.getSearchEntries())
{
final SCIMAttributeValue v =
createMemberValue(groupResolver, rEntry);
if (v != null)
{
values.add(v);
}
}
}
}
}
}
// Determine the 'type' and resource ID for static and virtual static
// groups.
if (members != null)
{
Map memberCache = null;
if (membersToCachePerRequest > 0)
{
memberCache = MEMBER_CACHES.get();
if (memberCache == null)
{
memberCache = new LinkedHashMap();
MEMBER_CACHES.set(memberCache);
}
}
for (final String memberDNString : members)
{
if ((userResolver != null &&
userResolver.isDnInScope(memberDNString)) ||
groupResolver.isDnInScope(memberDNString))
{
DN memberDN = new DN(memberDNString);
if (memberCache != null)
{
SCIMAttributeValue cacheValue = memberCache.get(memberDN);
if (cacheValue != null)
{
values.add(cacheValue);
continue;
}
}
final SearchRequest searchRequest =
new SearchRequest(memberDNString, SearchScope.BASE,
OBJECTCLASS_PRESENCE_FILTER, attrsToGet);
final SearchResult searchResult;
try
{
searchResult = ldapInterface.search(searchRequest);
}
catch (final LDAPSearchException lse)
{
Debug.debugException(lse);
continue;
}
if (searchResult.getEntryCount() == 1)
{
final SearchResultEntry rEntry =
searchResult.getSearchEntries().get(0);
final SCIMAttributeValue v =
createMemberValue(groupResolver, rEntry);
if (v != null)
{
if (memberCache != null)
{
memberCache.put(memberDN, v);
if (memberCache.size() > membersToCachePerRequest)
{
// We have cached too many members for this request, so we
// remove the oldest member from the cache.
Iterator it = memberCache.keySet().iterator();
it.next();
it.remove();
}
}
values.add(v);
}
}
}
}
}
}
catch (LDAPException e)
{
Debug.debugException(e);
throw ResourceMapper.toSCIMException(
"Error searching for values of the members attribute: " +
StaticUtils.getExceptionMessage(e), 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;
}
}
this.membersToCachePerRequest = 0;
Object o = getArguments().get(MAX_MEMBERS_CACHED);
if (o != null)
{
try
{
membersToCachePerRequest = Integer.valueOf(o.toString());
}
catch (NumberFormatException nfe)
{
Debug.debugException(nfe);
}
}
this.joinAttribute = null;
Object j = getArguments().get("joinAttribute");
if (j != null)
{
if (j.toString().equalsIgnoreCase("member"))
{
joinAttribute = LDAPJoinAttribute.MEMBER;
}
else if (j.toString().equalsIgnoreCase("uniqueMember"))
{
joinAttribute = LDAPJoinAttribute.UNIQUE_MEMBER;
}
else if (j.toString().equalsIgnoreCase("memberURL"))
{
joinAttribute = LDAPJoinAttribute.MEMBER_URL;
}
}
}
@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.
*/
protected 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'");
}
if (joinAttribute == null)
{
throw new InvalidResourceException(
"Member type is not valid. Member type must be of type " +
"'member', 'uniqueMember', or 'memberURL'");
}
switch (joinAttribute)
{
case MEMBER:
attributes.add(new Attribute(ATTR_MEMBER, dn));
break;
case UNIQUE_MEMBER:
attributes.add(new Attribute(ATTR_UNIQUE_MEMBER, dn));
break;
case MEMBER_URL:
attributes.add(new Attribute(ATTR_MEMBER_URL, dn));
break;
default:
throw new InvalidResourceException(
"Member type is not valid. Member type must be of type " +
"'member', 'uniqueMember', or 'memberURL'");
}
}
catch (Exception e)
{
Debug.debugException(e);
throw new InvalidResourceException(e.getMessage());
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public Set toLDAPAttributeTypes(final AttributePath scimAttribute)
throws InvalidResourceException
{
String subAttributeName = scimAttribute.getSubAttributeName();
if (subAttributeName != null)
{
// Just to make sure the sub-attribute is a valid one for this attribute.
descriptor.getSubAttribute(subAttributeName);
}
return Collections.singleton(ATTR_UNIQUE_MEMBER);
}
/**
* {@inheritDoc}
*/
@Override
public Filter toLDAPFilter(final SCIMFilter filter,
final LDAPRequestInterface ldapInterface,
final LDAPSearchResolver groupResolver)
throws InvalidResourceException
{
final SCIMFilterType filterType = filter.getFilterType();
final String filterValue = filter.getFilterValue();
// Determine the DN for this member.
try
{
switch (filterType)
{
// We don't have to worry about AND and OR filter types since they are
// handled earlier by the resource mapper.
case EQUALITY:
{
String dn = null;
if (userResolver != null)
{
try
{
dn = userResolver.getDnFromId(ldapInterface, filterValue);
}
catch (ResourceNotFoundException e)
{
// That's OK. It might be a group.
}
}
if (dn == null)
{
try
{
dn = groupResolver.getDnFromId(ldapInterface, filterValue);
}
catch (ResourceNotFoundException e)
{
// Value is not a valid user or group. Will not match anything.
return null;
}
}
final List filters = new ArrayList(2);
filters.add(Filter.createEqualityFilter(ATTR_MEMBER, dn));
filters.add(Filter.createEqualityFilter(ATTR_UNIQUE_MEMBER, dn));
filters.add(Filter.createPresenceFilter(ATTR_MEMBER_URL));
return Filter.createORFilter(filters);
}
default:
throw new InvalidResourceException(
"Filter type " + filterType + " is not supported for attribute " +
getAttributeDescriptor().getName());
}
}
catch (Exception e)
{
Debug.debugException(e);
throw new InvalidResourceException(e.getMessage());
}
}
/**
* Clear the cache.
*/
static void clearRequestCache()
{
MEMBER_CACHES.remove();
}
}