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 019import java.util.LinkedHashSet; 020import java.util.List; 021import java.util.Set; 022 023import org.forgerock.i18n.LocalizableMessage; 024import org.forgerock.i18n.slf4j.LocalizedLogger; 025import org.forgerock.opendj.config.server.ConfigChangeResult; 026import org.forgerock.opendj.config.server.ConfigException; 027import org.forgerock.opendj.config.server.ConfigurationChangeListener; 028import org.forgerock.opendj.ldap.schema.AttributeType; 029import org.forgerock.opendj.ldap.schema.ObjectClass; 030import org.forgerock.opendj.server.config.meta.PluginCfgDefn; 031import org.forgerock.opendj.server.config.server.LDAPAttributeDescriptionListPluginCfg; 032import org.forgerock.opendj.server.config.server.PluginCfg; 033import org.opends.server.api.plugin.DirectoryServerPlugin; 034import org.opends.server.api.plugin.PluginResult; 035import org.opends.server.api.plugin.PluginType; 036import org.opends.server.types.DirectoryConfig; 037import org.opends.server.types.operation.PreParseSearchOperation; 038 039import static org.opends.messages.PluginMessages.*; 040import static org.opends.server.core.DirectoryServer.*; 041import static org.opends.server.util.ServerConstants.*; 042 043/** 044 * This pre-parse plugin modifies the operation to allow an object class 045 * identifier to be specified in attributes lists, such as in Search requests, 046 * to request the return all attributes belonging to an object class as per the 047 * specification in RFC 4529. The "@" character is used to distinguish an 048 * object class identifier from an attribute descriptions. 049 */ 050public final class LDAPADListPlugin 051 extends DirectoryServerPlugin<LDAPAttributeDescriptionListPluginCfg> 052 implements ConfigurationChangeListener< 053 LDAPAttributeDescriptionListPluginCfg> 054{ 055 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 056 057 058 059 /** 060 * Filters the set of attributes provided in a search request or pre- / post- 061 * read controls according to RFC 4529. More specifically, this method 062 * iterates through the requested attributes to see if any of them reference 063 * an object class, as indicated by a "@" prefix, and substitutes the object 064 * class reference with the attribute types contained in the object class, as 065 * well as any of the attribute types contained in any superior object 066 * classes. 067 * 068 * @param attributes 069 * The attribute list to be normalized. 070 * @return The normalized attribute list. 071 */ 072 public static Set<String> normalizedObjectClasses(Set<String> attributes) 073 { 074 boolean foundOC = false; 075 for (String attrName : attributes) 076 { 077 if (attrName.startsWith("@")) 078 { 079 foundOC = true; 080 break; 081 } 082 } 083 084 if (foundOC) 085 { 086 final LinkedHashSet<String> newAttrs = new LinkedHashSet<>(); 087 for (final String attrName : attributes) 088 { 089 if (attrName.startsWith("@")) 090 { 091 final String ocName = attrName.substring(1); 092 final ObjectClass oc = getSchema().getObjectClass(ocName); 093 if (oc.isPlaceHolder()) 094 { 095 logger.trace("Cannot replace unknown objectclass %s", ocName); 096 } 097 else 098 { 099 logger.trace("Replacing objectclass %s", ocName); 100 101 for (final AttributeType at : oc.getRequiredAttributes()) 102 { 103 newAttrs.add(at.getNameOrOID()); 104 } 105 106 for (final AttributeType at : oc.getOptionalAttributes()) 107 { 108 newAttrs.add(at.getNameOrOID()); 109 } 110 } 111 } 112 else 113 { 114 newAttrs.add(attrName); 115 } 116 } 117 attributes = newAttrs; 118 } 119 120 return attributes; 121 } 122 123 124 125 /** The current configuration for this plugin. */ 126 private LDAPAttributeDescriptionListPluginCfg currentConfig; 127 128 129 130 /** 131 * Creates a new instance of this Directory Server plugin. Every plugin must 132 * implement a default constructor (it is the only one that will be used to 133 * create plugins defined in the configuration), and every plugin constructor 134 * must call <CODE>super()</CODE> as its first element. 135 */ 136 public LDAPADListPlugin() 137 { 138 super(); 139 } 140 141 142 143 @Override 144 public final void initializePlugin(Set<PluginType> pluginTypes, 145 LDAPAttributeDescriptionListPluginCfg configuration) 146 throws ConfigException 147 { 148 currentConfig = configuration; 149 configuration.addLDAPAttributeDescriptionListChangeListener(this); 150 151 // The set of plugin types must contain only the pre-parse search element. 152 if (pluginTypes.isEmpty()) 153 { 154 throw new ConfigException(ERR_PLUGIN_ADLIST_NO_PLUGIN_TYPES.get(configuration.dn())); 155 } 156 else 157 { 158 for (PluginType t : pluginTypes) 159 { 160 if (t != PluginType.PRE_PARSE_SEARCH) 161 { 162 throw new ConfigException(ERR_PLUGIN_ADLIST_INVALID_PLUGIN_TYPE.get(configuration.dn(), t)); 163 } 164 } 165 } 166 167 168 // Register the appropriate supported feature with the Directory Server. 169 DirectoryConfig.registerSupportedFeature(OID_LDAP_ADLIST_FEATURE); 170 } 171 172 173 174 @Override 175 public final void finalizePlugin() 176 { 177 currentConfig.removeLDAPAttributeDescriptionListChangeListener(this); 178 } 179 180 181 182 @Override 183 public final PluginResult.PreParse doPreParse( 184 PreParseSearchOperation searchOperation) 185 { 186 searchOperation.setAttributes(normalizedObjectClasses(searchOperation 187 .getAttributes())); 188 return PluginResult.PreParse.continueOperationProcessing(); 189 } 190 191 192 193 @Override 194 public boolean isConfigurationAcceptable(PluginCfg configuration, 195 List<LocalizableMessage> unacceptableReasons) 196 { 197 LDAPAttributeDescriptionListPluginCfg cfg = 198 (LDAPAttributeDescriptionListPluginCfg) configuration; 199 return isConfigurationChangeAcceptable(cfg, unacceptableReasons); 200 } 201 202 203 204 @Override 205 public boolean isConfigurationChangeAcceptable( 206 LDAPAttributeDescriptionListPluginCfg configuration, 207 List<LocalizableMessage> unacceptableReasons) 208 { 209 boolean configAcceptable = true; 210 211 // Ensure that the set of plugin types contains only pre-parse search. 212 for (PluginCfgDefn.PluginType pluginType : configuration.getPluginType()) 213 { 214 switch (pluginType) 215 { 216 case PREPARSESEARCH: 217 // This is acceptable. 218 break; 219 220 221 default: 222 unacceptableReasons.add(ERR_PLUGIN_ADLIST_INVALID_PLUGIN_TYPE.get(configuration.dn(), pluginType)); 223 configAcceptable = false; 224 } 225 } 226 227 return configAcceptable; 228 } 229 230 231 232 @Override 233 public ConfigChangeResult applyConfigurationChange( 234 LDAPAttributeDescriptionListPluginCfg 235 configuration) 236 { 237 currentConfig = configuration; 238 return new ConfigChangeResult(); 239 } 240} 241