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