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