001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2008 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS. 026 */ 027package org.opends.server.plugins; 028 029 030import java.util.LinkedHashSet; 031import java.util.List; 032import java.util.Set; 033 034import org.forgerock.i18n.LocalizableMessage; 035import org.opends.server.admin.server.ConfigurationChangeListener; 036import org.opends.server.admin.std.meta.PluginCfgDefn; 037import org.opends.server.admin.std.server.LDAPAttributeDescriptionListPluginCfg; 038import org.opends.server.admin.std.server.PluginCfg; 039import org.opends.server.api.plugin.DirectoryServerPlugin; 040import org.opends.server.api.plugin.PluginType; 041import org.opends.server.api.plugin.PluginResult; 042import org.forgerock.opendj.config.server.ConfigException; 043import org.opends.server.types.AttributeType; 044import org.forgerock.opendj.config.server.ConfigChangeResult; 045import org.opends.server.types.DirectoryConfig; 046import org.opends.server.types.ObjectClass; 047import org.opends.server.types.operation.PreParseSearchOperation; 048 049import org.forgerock.i18n.slf4j.LocalizedLogger; 050import static org.opends.messages.PluginMessages.*; 051 052import static org.opends.server.types.DirectoryConfig.getObjectClass; 053import static org.opends.server.util.ServerConstants.*; 054import static org.opends.server.util.StaticUtils.*; 055 056 057/** 058 * This pre-parse plugin modifies the operation to allow an object class 059 * identifier to be specified in attributes lists, such as in Search requests, 060 * to request the return all attributes belonging to an object class as per the 061 * specification in RFC 4529. The "@" character is used to distinguish an 062 * object class identifier from an attribute descriptions. 063 */ 064public final class LDAPADListPlugin 065 extends DirectoryServerPlugin<LDAPAttributeDescriptionListPluginCfg> 066 implements ConfigurationChangeListener< 067 LDAPAttributeDescriptionListPluginCfg> 068{ 069 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 070 071 072 073 /** 074 * Filters the set of attributes provided in a search request or pre- / post- 075 * read controls according to RFC 4529. More specifically, this method 076 * iterates through the requested attributes to see if any of them reference 077 * an object class, as indicated by a "@" prefix, and substitutes the object 078 * class reference with the attribute types contained in the object class, as 079 * well as any of the attribute types contained in any superior object 080 * classes. 081 * 082 * @param attributes 083 * The attribute list to be normalized. 084 * @return The normalized attribute list. 085 */ 086 public static Set<String> normalizedObjectClasses(Set<String> attributes) 087 { 088 boolean foundOC = false; 089 for (String attrName : attributes) 090 { 091 if (attrName.startsWith("@")) 092 { 093 foundOC = true; 094 break; 095 } 096 } 097 098 if (foundOC) 099 { 100 final LinkedHashSet<String> newAttrs = new LinkedHashSet<>(); 101 for (final String attrName : attributes) 102 { 103 if (attrName.startsWith("@")) 104 { 105 final String lowerName = toLowerCase(attrName.substring(1)); 106 final ObjectClass oc = getObjectClass(lowerName, false); 107 if (oc == null) 108 { 109 if (logger.isTraceEnabled()) 110 { 111 logger.trace("Cannot replace unknown objectclass %s", 112 lowerName); 113 } 114 } 115 else 116 { 117 if (logger.isTraceEnabled()) 118 { 119 logger.trace("Replacing objectclass %s", lowerName); 120 } 121 122 for (final AttributeType at : oc.getRequiredAttributeChain()) 123 { 124 newAttrs.add(at.getNameOrOID()); 125 } 126 127 for (final AttributeType at : oc.getOptionalAttributeChain()) 128 { 129 newAttrs.add(at.getNameOrOID()); 130 } 131 } 132 } 133 else 134 { 135 newAttrs.add(attrName); 136 } 137 } 138 attributes = newAttrs; 139 } 140 141 return attributes; 142 } 143 144 145 146 /** The current configuration for this plugin. */ 147 private LDAPAttributeDescriptionListPluginCfg currentConfig; 148 149 150 151 /** 152 * Creates a new instance of this Directory Server plugin. Every plugin must 153 * implement a default constructor (it is the only one that will be used to 154 * create plugins defined in the configuration), and every plugin constructor 155 * must call <CODE>super()</CODE> as its first element. 156 */ 157 public LDAPADListPlugin() 158 { 159 super(); 160 } 161 162 163 164 /** {@inheritDoc} */ 165 @Override 166 public final void initializePlugin(Set<PluginType> pluginTypes, 167 LDAPAttributeDescriptionListPluginCfg configuration) 168 throws ConfigException 169 { 170 currentConfig = configuration; 171 configuration.addLDAPAttributeDescriptionListChangeListener(this); 172 173 // The set of plugin types must contain only the pre-parse search element. 174 if (pluginTypes.isEmpty()) 175 { 176 throw new ConfigException(ERR_PLUGIN_ADLIST_NO_PLUGIN_TYPES.get(configuration.dn())); 177 } 178 else 179 { 180 for (PluginType t : pluginTypes) 181 { 182 if (t != PluginType.PRE_PARSE_SEARCH) 183 { 184 throw new ConfigException(ERR_PLUGIN_ADLIST_INVALID_PLUGIN_TYPE.get(configuration.dn(), t)); 185 } 186 } 187 } 188 189 190 // Register the appropriate supported feature with the Directory Server. 191 DirectoryConfig.registerSupportedFeature(OID_LDAP_ADLIST_FEATURE); 192 } 193 194 195 196 /** {@inheritDoc} */ 197 @Override 198 public final void finalizePlugin() 199 { 200 currentConfig.removeLDAPAttributeDescriptionListChangeListener(this); 201 } 202 203 204 205 /** {@inheritDoc} */ 206 @Override 207 public final PluginResult.PreParse doPreParse( 208 PreParseSearchOperation searchOperation) 209 { 210 searchOperation.setAttributes(normalizedObjectClasses(searchOperation 211 .getAttributes())); 212 return PluginResult.PreParse.continueOperationProcessing(); 213 } 214 215 216 217 /** {@inheritDoc} */ 218 @Override 219 public boolean isConfigurationAcceptable(PluginCfg configuration, 220 List<LocalizableMessage> unacceptableReasons) 221 { 222 LDAPAttributeDescriptionListPluginCfg cfg = 223 (LDAPAttributeDescriptionListPluginCfg) configuration; 224 return isConfigurationChangeAcceptable(cfg, unacceptableReasons); 225 } 226 227 228 229 /** {@inheritDoc} */ 230 public boolean isConfigurationChangeAcceptable( 231 LDAPAttributeDescriptionListPluginCfg configuration, 232 List<LocalizableMessage> unacceptableReasons) 233 { 234 boolean configAcceptable = true; 235 236 // Ensure that the set of plugin types contains only pre-parse search. 237 for (PluginCfgDefn.PluginType pluginType : configuration.getPluginType()) 238 { 239 switch (pluginType) 240 { 241 case PREPARSESEARCH: 242 // This is acceptable. 243 break; 244 245 246 default: 247 unacceptableReasons.add(ERR_PLUGIN_ADLIST_INVALID_PLUGIN_TYPE.get(configuration.dn(), pluginType)); 248 configAcceptable = false; 249 } 250 } 251 252 return configAcceptable; 253 } 254 255 256 257 /** {@inheritDoc} */ 258 public ConfigChangeResult applyConfigurationChange( 259 LDAPAttributeDescriptionListPluginCfg 260 configuration) 261 { 262 currentConfig = configuration; 263 return new ConfigChangeResult(); 264 } 265} 266