All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.scim.ldap.GroupsDerivedAttribute Maven / Gradle / Ivy

Go to download

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.

There is a newer version: 1.8.26
Show newest version
/*
 * 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.DN;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.Filter;
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.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.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;



/**
 * This class provides a derived attribute implementation for the groups
 * attribute in User resources, which may be used when the directory server
 * does not provide the isMemberOf LDAP attribute. The groups are derived by
 * searching the DIT for static group entries whose members include the DN
 * of the User entry.
 * 

* 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 entries. */ public class GroupsDerivedAttribute extends DerivedAttribute { /** * The name of the LDAP cn attribute. */ private static final String ATTR_CN = "cn"; /** * The name of the LDAP objectClass attribute. */ private static final String ATTR_OBJECT_CLASS = "objectClass"; /** * The name of the LDAP isMemberOf attribute. */ private static final String ATTR_IS_MEMBER_OF = "isMemberOf"; /** * The name of the LDAP member attribute. */ private static final String ATTR_MEMBER = "member"; /** * The name of the LDAP memberURL attribute. */ private static final String ATTR_MEMBER_URL = "memberURL"; /** * The name of the LDAP uniqueMember attribute. */ private static final String ATTR_UNIQUE_MEMBER = "uniqueMember"; /** * The name of the groupOfURLs object class. */ private static final String OC_GROUP_OF_URLS = "groupOfURLs"; /** * The name of the ds-virtual-static-group object class. */ private static final String OC_VIRTUAL_STATIC_GROUP = "ds-virtual-static-group"; /** * The "direct" canonical value for the type sub-attribute. */ private static final String DIRECT_GROUP = "direct"; /** * The "indirect" canonical value for the type sub-attribute. */ private static final String INDIRECT_GROUP = "indirect"; /** * The attribute descriptor for the derived attribute. */ private AttributeDescriptor descriptor; /** * The LDAPSearchResolver to use when looking for groups to which * a certain user belongs. */ private LDAPSearchResolver groupResolver; @Override public Set getLDAPAttributeTypes() { return Collections.singleton(ATTR_IS_MEMBER_OF); } @Override public SCIMAttribute toSCIMAttribute(final Entry entry, final LDAPRequestInterface ldapInterface, final LDAPSearchResolver userResolver) throws SCIMException { final List values = new ArrayList(); if (groupResolver == null) { Debug.debug(Level.WARNING, DebugType.OTHER, "Cannot provide the groups attribute because there are " + "no LDAPSearch parameters"); return null; } try { if (entry.hasAttribute(ATTR_IS_MEMBER_OF)) { final List attrList = new ArrayList(3); attrList.add(ATTR_CN); attrList.add(ATTR_OBJECT_CLASS); groupResolver.addIdAttribute(attrList); final String[] attrsToGet = attrList.toArray(new String[attrList.size()]); // We can use the isMemberOf attribute for (final String dnString : entry.getAttributeValues(ATTR_IS_MEMBER_OF)) { // Make sure the group is scoped within the base DN. if (groupResolver.isDnInScope(dnString)) { // Retrieve the group entry and passing in the search param filter // if available. SearchRequest searchRequest = new SearchRequest(dnString, SearchScope.BASE, groupResolver.getFilterString(), attrsToGet); searchRequest.setSizeLimit(1); SearchResultEntry groupEntry = ldapInterface.searchForEntry(searchRequest); if(groupEntry != null) { // This group is considered direct iff it is a non-virtual // static group and the entry is listed as a member or // uniqueMember of this group (i.e. it's not nested). boolean isDirect = false; if(!groupEntry.hasObjectClass(OC_GROUP_OF_URLS) && !groupEntry.hasObjectClass(OC_VIRTUAL_STATIC_GROUP)) { // Make sure the entry DN is listed as a member or // uniqueMember. searchRequest = new SearchRequest(dnString, SearchScope.BASE, groupsFilter(entry.getDN(), false), "1.1"); searchRequest.setSizeLimit(1); isDirect = ldapInterface.searchForEntry(searchRequest) != null; } final String resourceID = groupResolver.getIdFromEntry(groupEntry); values.add(createGroupValue(resourceID, groupEntry.getAttributeValue(ATTR_CN), isDirect)); } } } } else { // We can't use isMemberOf so we'll have to find all group entries // that satisfies the search param. This should give us all static // groups (including virtual static groups) that the entry is a member // of as well as all dynamic groups that satisfy the search params. findGroupsForMember(entry, ldapInterface, groupResolver.getBaseDN(), groupsFilter(entry.getDN(), true), values, new LinkedList(), false); } } catch (LDAPException e) { Debug.debugException(e); throw ResourceMapper.toSCIMException( "Error searching for values of the groups attribute: " + e.getMessage(), e); } if (values.isEmpty()) { return null; } else { return SCIMAttribute.create( getAttributeDescriptor(), values.toArray(new SCIMAttributeValue[values.size()])); } } /** * {@inheritDoc} */ @Override public void toLDAPAttributes(final SCIMObject scimObject, final Collection attributes, final LDAPRequestInterface ldapInterface, final LDAPSearchResolver groupResolver) throws SCIMException { // No implementation required because this attribute is read-only. } @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) { groupResolver = (LDAPSearchResolver) o; } } } @Override public AttributeDescriptor getAttributeDescriptor() { return descriptor; } /** * Construct a filter that could be used to find all static groups with the * provided member DN (and optionally any dynamic groups as well). * * @param memberDN The member DN used to determining the static groups for * which it belongs. * @param includeDynamicGroups Whether dynamic groups should be included. * * @return A filter that could be used to find all static groups with the * provided member DN. * @throws LDAPException if an error occurs while parsing the filter from * searchParams. */ private Filter groupsFilter(final String memberDN, final boolean includeDynamicGroups) throws LDAPException { Filter filter = null; if(groupResolver != null) { //This will be a filter that handles all the Group object classes filter = Filter.create(groupResolver.getFilterString()); } List memberFilters = new ArrayList(3); memberFilters.add(Filter.createEqualityFilter(ATTR_MEMBER, memberDN)); memberFilters.add( Filter.createEqualityFilter(ATTR_UNIQUE_MEMBER, memberDN)); if(includeDynamicGroups) { memberFilters.add( Filter.createEqualityFilter(ATTR_OBJECT_CLASS, OC_GROUP_OF_URLS)); } if(filter != null) { return Filter.createANDFilter(filter, Filter.createORFilter(memberFilters)); } else { return Filter.createORFilter(memberFilters); } } /** * Add all group entries returned from the provided search parameters and * recursively find all nested groups as well. * * @param entry An LDAP entry representing the SCIM resource for * which a SCIM attribute value is to be derived. * @param ldapInterface An LDAP interface that may be used to search the DIT. * @param baseDN The search base DN for the DIT. * @param filter The filter to search the DIT. * @param values The values of the groups attribute. * @param visitedGroups Groups that were already visited. * @param nested Whether the groups found are nested. * @throws LDAPException if an error occurs while performing the search. * @throws InvalidResourceException if the mapping violates the schema. */ private void findGroupsForMember(final Entry entry, final LDAPRequestInterface ldapInterface, final String baseDN, final Filter filter, final List values, final List visitedGroups, final boolean nested) throws LDAPException, InvalidResourceException { final List attrList = new ArrayList(4); attrList.add(ATTR_CN); attrList.add(ATTR_OBJECT_CLASS); attrList.add(ATTR_MEMBER_URL); groupResolver.addIdAttribute(attrList); final String[] attrsToGet = attrList.toArray(new String[attrList.size()]); // Find all groups final SearchRequest searchRequest = new SearchRequest(baseDN, SearchScope.SUB, filter, attrsToGet); final SearchResult searchResult = ldapInterface.search(searchRequest); List entriesToVisit = new ArrayList(searchResult.getEntryCount()); for (final SearchResultEntry resultEntry : searchResult.getSearchEntries()) { // Make sure we haven't visited this group before. DN groupDN = resultEntry.getParsedDN(); if(!visitedGroups.contains(groupDN)) { visitedGroups.add(groupDN); entriesToVisit.add(resultEntry); final String resourceID = groupResolver.getIdFromEntry(resultEntry); if(resultEntry.hasObjectClass(OC_GROUP_OF_URLS)) { // This is a dynamic group, see if the entry should be a member String memberUrl = resultEntry.getAttributeValue(ATTR_MEMBER_URL); if(memberUrl != null) { LDAPURL url = new LDAPURL(memberUrl); if(entry.matchesBaseAndScope(url.getBaseDN(), url.getScope()) && url.getFilter().matchesEntry(entry)) { values.add(createGroupValue(resourceID, resultEntry.getAttributeValue(ATTR_CN), false)); } } } else { // This is a static group that we are a member of. values.add(createGroupValue(resourceID, resultEntry.getAttributeValue(ATTR_CN), !nested && !resultEntry.hasObjectClass(OC_VIRTUAL_STATIC_GROUP))); } } } for (final SearchResultEntry resultEntry : entriesToVisit) { // Recursively find all groups that nest this group. findGroupsForMember(entry, ldapInterface, baseDN, groupsFilter(resultEntry.getDN(), false), values, visitedGroups, true); } } /** * Create a value for the groups multi-valued attribute. * * @param id The ID of group. * @param displayName The displayName of the group. * @param isDirect Whether the group is direct or indirect. * @return The constructed SCIMAttributeValue for the groups multi-valued * attribute. * @throws InvalidResourceException if the mapping violates the schema. */ private SCIMAttributeValue createGroupValue(final String id, final String displayName, final boolean isDirect) throws InvalidResourceException { final List subAttributes = new ArrayList(3); subAttributes.add( SCIMAttribute.create( getAttributeDescriptor().getSubAttribute("value"), SCIMAttributeValue.createStringValue(id))); if (displayName != null) { subAttributes.add( SCIMAttribute.create( getAttributeDescriptor().getSubAttribute("display"), SCIMAttributeValue.createStringValue(displayName))); } subAttributes.add( SCIMAttribute.create( getAttributeDescriptor().getSubAttribute("type"), SCIMAttributeValue.createStringValue( isDirect ? DIRECT_GROUP : INDIRECT_GROUP))); return SCIMAttributeValue.createComplexValue(subAttributes); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy