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 2014-2015 ForgeRock AS 026 */ 027package org.opends.server.plugins; 028 029import java.util.List; 030import java.util.Set; 031 032import org.forgerock.i18n.LocalizableMessage; 033import org.forgerock.i18n.slf4j.LocalizedLogger; 034import org.forgerock.opendj.config.server.ConfigChangeResult; 035import org.forgerock.opendj.config.server.ConfigException; 036import org.forgerock.opendj.ldap.ByteString; 037import org.forgerock.opendj.ldap.ModificationType; 038import org.opends.server.admin.server.ConfigurationChangeListener; 039import org.opends.server.admin.std.meta.PluginCfgDefn; 040import org.opends.server.admin.std.server.LastModPluginCfg; 041import org.opends.server.admin.std.server.PluginCfg; 042import org.opends.server.api.plugin.DirectoryServerPlugin; 043import org.opends.server.api.plugin.PluginResult; 044import org.opends.server.api.plugin.PluginType; 045import org.opends.server.core.DirectoryServer; 046import org.opends.server.types.*; 047import org.opends.server.types.operation.PreOperationAddOperation; 048import org.opends.server.types.operation.PreOperationModifyDNOperation; 049import org.opends.server.types.operation.PreOperationModifyOperation; 050 051import static org.opends.messages.PluginMessages.*; 052import static org.opends.server.config.ConfigConstants.*; 053import static org.opends.server.util.TimeThread.*; 054 055/** 056 * This class implements a Directory Server plugin that will add the 057 * creatorsName and createTimestamp attributes to an entry whenever it is added 058 * to the server, and will add the modifiersName and modifyTimestamp attributes 059 * whenever the entry is modified or renamed. 060 */ 061public final class LastModPlugin 062 extends DirectoryServerPlugin<LastModPluginCfg> 063 implements ConfigurationChangeListener<LastModPluginCfg> 064{ 065 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 066 067 /** The attribute type for the "createTimestamp" attribute. */ 068 private final AttributeType createTimestampType; 069 /** The attribute type for the "creatorsName" attribute. */ 070 private final AttributeType creatorsNameType; 071 /** The attribute type for the "modifiersName" attribute. */ 072 private final AttributeType modifiersNameType; 073 /** The attribute type for the "modifyTimestamp" attribute. */ 074 private final AttributeType modifyTimestampType; 075 /** The current configuration for this plugin. */ 076 private LastModPluginCfg currentConfig; 077 078 079 /** 080 * Creates a new instance of this Directory Server plugin. Every plugin must 081 * implement a default constructor (it is the only one that will be used to 082 * create plugins defined in the configuration), and every plugin constructor 083 * must call <CODE>super()</CODE> as its first element. 084 */ 085 public LastModPlugin() 086 { 087 super(); 088 089 090 // Get the attribute types for the attributes that we will use. This needs 091 // to be done in the constructor in order to make the associated variables "final". 092 createTimestampType = DirectoryServer.getAttributeTypeOrDefault(OP_ATTR_CREATE_TIMESTAMP_LC); 093 creatorsNameType = DirectoryServer.getAttributeTypeOrDefault(OP_ATTR_CREATORS_NAME_LC); 094 modifiersNameType = DirectoryServer.getAttributeTypeOrDefault(OP_ATTR_MODIFIERS_NAME_LC); 095 modifyTimestampType = DirectoryServer.getAttributeTypeOrDefault(OP_ATTR_MODIFY_TIMESTAMP_LC); 096 } 097 098 099 100 /** {@inheritDoc} */ 101 @Override 102 public final void initializePlugin(Set<PluginType> pluginTypes, 103 LastModPluginCfg configuration) 104 throws ConfigException 105 { 106 currentConfig = configuration; 107 configuration.addLastModChangeListener(this); 108 109 // Make sure that the plugin has been enabled for the appropriate types. 110 for (PluginType t : pluginTypes) 111 { 112 switch (t) 113 { 114 case PRE_OPERATION_ADD: 115 case PRE_OPERATION_MODIFY: 116 case PRE_OPERATION_MODIFY_DN: 117 // These are acceptable. 118 break; 119 120 default: 121 throw new ConfigException(ERR_PLUGIN_LASTMOD_INVALID_PLUGIN_TYPE.get(t)); 122 } 123 } 124 } 125 126 127 128 /** {@inheritDoc} */ 129 @Override 130 public final void finalizePlugin() 131 { 132 currentConfig.removeLastModChangeListener(this); 133 } 134 135 136 137 /** {@inheritDoc} */ 138 @Override 139 public final PluginResult.PreOperation 140 doPreOperation(PreOperationAddOperation addOperation) 141 { 142 // Create the attribute list for the creatorsName attribute, if appropriate. 143 AttributeBuilder builder = new AttributeBuilder(creatorsNameType, 144 OP_ATTR_CREATORS_NAME); 145 DN creatorDN = addOperation.getAuthorizationDN(); 146 if (creatorDN == null) 147 { 148 // This must mean that the operation was performed anonymously. 149 // Even so, we still need to update the creatorsName attribute. 150 builder.add(ByteString.empty()); 151 } 152 else 153 { 154 builder.add(creatorDN.toString()); 155 } 156 addOperation.setAttribute(creatorsNameType, builder.toAttributeList()); 157 158 159 // Create the attribute list for the createTimestamp attribute. 160 List<Attribute> timeList = Attributes.createAsList( 161 createTimestampType, OP_ATTR_CREATE_TIMESTAMP, getGMTTime()); 162 addOperation.setAttribute(createTimestampType, timeList); 163 164 // We shouldn't ever need to return a non-success result. 165 return PluginResult.PreOperation.continueOperationProcessing(); 166 } 167 168 169 170 /** {@inheritDoc} */ 171 @Override 172 public final PluginResult.PreOperation 173 doPreOperation(PreOperationModifyOperation modifyOperation) 174 { 175 // Create the modifiersName attribute. 176 AttributeBuilder builder = new AttributeBuilder(modifiersNameType, 177 OP_ATTR_MODIFIERS_NAME); 178 DN modifierDN = modifyOperation.getAuthorizationDN(); 179 if (modifierDN == null) 180 { 181 // This must mean that the operation was performed anonymously. 182 // Even so, we still need to update the modifiersName attribute. 183 builder.add(ByteString.empty()); 184 } 185 else 186 { 187 builder.add(modifierDN.toString()); 188 } 189 Attribute nameAttr = builder.toAttribute(); 190 try 191 { 192 modifyOperation.addModification(new Modification(ModificationType.REPLACE, 193 nameAttr, true)); 194 } 195 catch (DirectoryException de) 196 { 197 logger.traceException(de); 198 199 // This should never happen. 200 return PluginResult.PreOperation.stopProcessing( 201 DirectoryConfig.getServerErrorResultCode(), de.getMessageObject()); 202 } 203 204 205 // Create the modifyTimestamp attribute. 206 Attribute timeAttr = Attributes.create(modifyTimestampType, 207 OP_ATTR_MODIFY_TIMESTAMP, getGMTTime()); 208 try 209 { 210 modifyOperation.addModification(new Modification(ModificationType.REPLACE, 211 timeAttr, true)); 212 } 213 catch (DirectoryException de) 214 { 215 logger.traceException(de); 216 217 // This should never happen. 218 return PluginResult.PreOperation.stopProcessing( 219 DirectoryConfig.getServerErrorResultCode(), de.getMessageObject()); 220 } 221 222 223 // We shouldn't ever need to return a non-success result. 224 return PluginResult.PreOperation.continueOperationProcessing(); 225 } 226 227 228 229 /** {@inheritDoc} */ 230 @Override 231 public final PluginResult.PreOperation 232 doPreOperation(PreOperationModifyDNOperation modifyDNOperation) 233 { 234 // Create the modifiersName attribute. 235 AttributeBuilder builder = new AttributeBuilder(modifiersNameType, 236 OP_ATTR_MODIFIERS_NAME); 237 DN modifierDN = modifyDNOperation.getAuthorizationDN(); 238 if (modifierDN == null) 239 { 240 // This must mean that the operation was performed anonymously. 241 // Even so, we still need to update the modifiersName attribute. 242 builder.add(ByteString.empty()); 243 } 244 else 245 { 246 builder.add(modifierDN.toString()); 247 } 248 Attribute nameAttr = builder.toAttribute(); 249 modifyDNOperation.addModification(new Modification( 250 ModificationType.REPLACE, nameAttr, true)); 251 252 253 // Create the modifyTimestamp attribute. 254 Attribute timeAttr = Attributes.create(modifyTimestampType, 255 OP_ATTR_MODIFY_TIMESTAMP, getGMTTime()); 256 modifyDNOperation.addModification(new Modification( 257 ModificationType.REPLACE, timeAttr, true)); 258 259 260 // We shouldn't ever need to return a non-success result. 261 return PluginResult.PreOperation.continueOperationProcessing(); 262 } 263 264 265 266 /** {@inheritDoc} */ 267 @Override 268 public boolean isConfigurationAcceptable(PluginCfg configuration, 269 List<LocalizableMessage> unacceptableReasons) 270 { 271 LastModPluginCfg cfg = (LastModPluginCfg) configuration; 272 return isConfigurationChangeAcceptable(cfg, unacceptableReasons); 273 } 274 275 276 277 /** {@inheritDoc} */ 278 @Override 279 public boolean isConfigurationChangeAcceptable(LastModPluginCfg configuration, 280 List<LocalizableMessage> unacceptableReasons) 281 { 282 boolean configAcceptable = true; 283 284 // Ensure that the set of plugin types contains only pre-operation add, 285 // pre-operation modify, and pre-operation modify DN. 286 for (PluginCfgDefn.PluginType pluginType : configuration.getPluginType()) 287 { 288 switch (pluginType) 289 { 290 case PREOPERATIONADD: 291 case PREOPERATIONMODIFY: 292 case PREOPERATIONMODIFYDN: 293 // These are acceptable. 294 break; 295 296 297 default: 298 unacceptableReasons.add(ERR_PLUGIN_LASTMOD_INVALID_PLUGIN_TYPE.get(pluginType)); 299 configAcceptable = false; 300 } 301 } 302 303 return configAcceptable; 304 } 305 306 307 308 /** {@inheritDoc} */ 309 @Override 310 public ConfigChangeResult applyConfigurationChange( 311 LastModPluginCfg configuration) 312 { 313 currentConfig = configuration; 314 return new ConfigChangeResult(); 315 } 316} 317