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