001/* 002 * Copyright 2016-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2016-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk.transformations; 022 023 024 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.HashSet; 029import java.util.Set; 030 031import com.unboundid.ldap.sdk.Attribute; 032import com.unboundid.ldap.sdk.Entry; 033import com.unboundid.ldap.sdk.Modification; 034import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition; 035import com.unboundid.ldap.sdk.schema.Schema; 036import com.unboundid.ldif.LDIFAddChangeRecord; 037import com.unboundid.ldif.LDIFChangeRecord; 038import com.unboundid.ldif.LDIFModifyChangeRecord; 039import com.unboundid.util.Debug; 040import com.unboundid.util.StaticUtils; 041import com.unboundid.util.ThreadSafety; 042import com.unboundid.util.ThreadSafetyLevel; 043 044 045 046/** 047 * This class provides an implementation of an entry and LDIF change record 048 * transformation that will remove a specified set of attributes from entries 049 * or change records. Note that this transformation will not alter entry DNs, 050 * so if an attribute to exclude is included in an entry's DN, that value will 051 * still be visible in the DN even if it is removed from the set of attributes 052 * in the entry. 053 */ 054@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 055public final class ExcludeAttributeTransformation 056 implements EntryTransformation, LDIFChangeRecordTransformation 057{ 058 // The schema to use when processing. 059 private final Schema schema; 060 061 // The set of attributes to exclude from entries. 062 private final Set<String> attributes; 063 064 065 066 /** 067 * Creates a new exclude attribute transformation that will strip the 068 * specified attributes out of entries and change records. 069 * 070 * @param schema The scheme to use to identify alternate names that 071 * may be used to reference the attributes to exclude from 072 * entries. It may be {@code null} to use a default 073 * standard schema. 074 * @param attributes The names of the attributes to strip from entries and 075 * change records. It must not be {@code null} or empty. 076 */ 077 public ExcludeAttributeTransformation(final Schema schema, 078 final String... attributes) 079 { 080 this(schema, StaticUtils.toList(attributes)); 081 } 082 083 084 085 /** 086 * Creates a new exclude attribute transformation that will strip the 087 * specified attributes out of entries and change records. 088 * 089 * @param schema The scheme to use to identify alternate names that 090 * may be used to reference the attributes to exclude from 091 * entries. It may be {@code null} to use a default 092 * standard schema. 093 * @param attributes The names of the attributes to strip from entries and 094 * change records. It must not be {@code null} or empty. 095 */ 096 public ExcludeAttributeTransformation(final Schema schema, 097 final Collection<String> attributes) 098 { 099 // If a schema was provided, then use it. Otherwise, use the default 100 // standard schema. 101 Schema s = schema; 102 if (s == null) 103 { 104 try 105 { 106 s = Schema.getDefaultStandardSchema(); 107 } 108 catch (final Exception e) 109 { 110 // This should never happen. 111 Debug.debugException(e); 112 } 113 } 114 this.schema = s; 115 116 117 // Identify all of the names that may be used to reference the attributes 118 // to suppress. 119 final HashSet<String> attrNames = new HashSet<String>(3*attributes.size()); 120 for (final String attrName : attributes) 121 { 122 final String baseName = 123 Attribute.getBaseName(StaticUtils.toLowerCase(attrName)); 124 attrNames.add(baseName); 125 126 if (s != null) 127 { 128 final AttributeTypeDefinition at = s.getAttributeType(baseName); 129 if (at != null) 130 { 131 attrNames.add(StaticUtils.toLowerCase(at.getOID())); 132 for (final String name : at.getNames()) 133 { 134 attrNames.add(StaticUtils.toLowerCase(name)); 135 } 136 } 137 } 138 } 139 this.attributes = Collections.unmodifiableSet(attrNames); 140 } 141 142 143 144 /** 145 * {@inheritDoc} 146 */ 147 @Override() 148 public Entry transformEntry(final Entry e) 149 { 150 if (e == null) 151 { 152 return null; 153 } 154 155 156 // First, see if the entry has any of the target attributes. If not, we can 157 // just return the provided entry. 158 boolean hasAttributeToRemove = false; 159 final Collection<Attribute> originalAttributes = e.getAttributes(); 160 for (final Attribute a : originalAttributes) 161 { 162 if (attributes.contains(StaticUtils.toLowerCase(a.getBaseName()))) 163 { 164 hasAttributeToRemove = true; 165 break; 166 } 167 } 168 169 if (! hasAttributeToRemove) 170 { 171 return e; 172 } 173 174 175 // Create a copy of the entry with all appropriate attributes removed. 176 final ArrayList<Attribute> attributesToKeep = 177 new ArrayList<Attribute>(originalAttributes.size()); 178 for (final Attribute a : originalAttributes) 179 { 180 if (! attributes.contains(StaticUtils.toLowerCase(a.getBaseName()))) 181 { 182 attributesToKeep.add(a); 183 } 184 } 185 186 return new Entry(e.getDN(), schema, attributesToKeep); 187 } 188 189 190 191 /** 192 * {@inheritDoc} 193 */ 194 @Override() 195 public LDIFChangeRecord transformChangeRecord(final LDIFChangeRecord r) 196 { 197 if (r == null) 198 { 199 return null; 200 } 201 202 203 // If it's an add change record, then just use the same processing as for an 204 // entry, except we will suppress the entire change record if all of the 205 // attributes end up getting suppressed. 206 if (r instanceof LDIFAddChangeRecord) 207 { 208 final LDIFAddChangeRecord addRecord = (LDIFAddChangeRecord) r; 209 final Entry updatedEntry = transformEntry(addRecord.getEntryToAdd()); 210 if (updatedEntry.getAttributes().isEmpty()) 211 { 212 return null; 213 } 214 215 return new LDIFAddChangeRecord(updatedEntry, addRecord.getControls()); 216 } 217 218 219 // If it's a modify change record, then suppress all modifications targeting 220 // any of the appropriate attributes. If there are no more modifications 221 // left, then suppress the entire change record. 222 if (r instanceof LDIFModifyChangeRecord) 223 { 224 final LDIFModifyChangeRecord modifyRecord = (LDIFModifyChangeRecord) r; 225 226 final Modification[] originalMods = modifyRecord.getModifications(); 227 final ArrayList<Modification> modsToKeep = 228 new ArrayList<Modification>(originalMods.length); 229 for (final Modification m : originalMods) 230 { 231 final String attrName = StaticUtils.toLowerCase( 232 Attribute.getBaseName(m.getAttributeName())); 233 if (! attributes.contains(attrName)) 234 { 235 modsToKeep.add(m); 236 } 237 } 238 239 if (modsToKeep.isEmpty()) 240 { 241 return null; 242 } 243 244 return new LDIFModifyChangeRecord(modifyRecord.getDN(), modsToKeep, 245 modifyRecord.getControls()); 246 } 247 248 249 // If it's some other type of change record (which should just be delete or 250 // modify DN), then don't do anything. 251 return r; 252 } 253 254 255 256 /** 257 * {@inheritDoc} 258 */ 259 @Override() 260 public Entry translate(final Entry original, final long firstLineNumber) 261 { 262 return transformEntry(original); 263 } 264 265 266 267 /** 268 * {@inheritDoc} 269 */ 270 @Override() 271 public LDIFChangeRecord translate(final LDIFChangeRecord original, 272 final long firstLineNumber) 273 { 274 return transformChangeRecord(original); 275 } 276 277 278 279 /** 280 * {@inheritDoc} 281 */ 282 @Override() 283 public Entry translateEntryToWrite(final Entry original) 284 { 285 return transformEntry(original); 286 } 287 288 289 290 /** 291 * {@inheritDoc} 292 */ 293 @Override() 294 public LDIFChangeRecord translateChangeRecordToWrite( 295 final LDIFChangeRecord original) 296 { 297 return transformChangeRecord(original); 298 } 299}