diff options
Diffstat (limited to 'src/connectors/src/data/io/ldap/LDAPFlatDataWriter.java')
-rw-r--r-- | src/connectors/src/data/io/ldap/LDAPFlatDataWriter.java | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/src/connectors/src/data/io/ldap/LDAPFlatDataWriter.java b/src/connectors/src/data/io/ldap/LDAPFlatDataWriter.java new file mode 100644 index 0000000..d1b8918 --- /dev/null +++ b/src/connectors/src/data/io/ldap/LDAPFlatDataWriter.java @@ -0,0 +1,198 @@ +/* + * SSSync, a Simple and Stupid Synchronizer for data with multi-valued attributes + * Copyright (C) 2014 Ludovic Pouzenc <ludovic@pouzenc.fr> + * + * This file is part of SSSync. + * + * SSSync is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SSSync is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with SSSync. If not, see <http://www.gnu.org/licenses/> + */ + +package data.io.ldap; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.unboundid.ldap.sdk.Attribute; +import com.unboundid.ldap.sdk.DN; +import com.unboundid.ldap.sdk.DeleteRequest; +import com.unboundid.ldap.sdk.Entry; +import com.unboundid.ldap.sdk.LDAPConnection; +import com.unboundid.ldap.sdk.LDAPException; +import com.unboundid.ldap.sdk.Modification; +import com.unboundid.ldap.sdk.ModificationType; +import com.unboundid.ldap.sdk.ModifyRequest; +import com.unboundid.ldap.sdk.RDN; +import com.unboundid.ldap.sdk.schema.EntryValidator; +import com.unboundid.ldif.LDIFException; + +import data.MVDataEntry; +import data.io.AbstractMVDataWriter; + +/** + * Stream-oriented LDAP writer from a particular LDAP Directory connection. + * + * @author lpouzenc + */ +public class LDAPFlatDataWriter extends AbstractMVDataWriter { + + private final LDAPConnection conn; + private final DN baseDN; + private final String keyAttr; + private final EntryValidator validator; + + /** + * Construct a new writer that could insert/update/delete entries on a particular LDAP connection and baseDN. + * + * @param conn Already initialized LDAP connection where run the search + * @param baseDN Search base DN (will return childs of this DN) + * @param keyAttr Attribute name that is the primary key of the entry, identifying the entry in a unique manner + * @throws LDAPException + */ + public LDAPFlatDataWriter(LDAPConnection conn, String baseDN, String keyAttr) throws LDAPException { + this.conn = conn; + this.baseDN = new DN(baseDN); + this.keyAttr = keyAttr; + this.validator = new EntryValidator(conn.getSchema()); + } + + /** + * {@inheritDoc} + */ + @Override + public void insert(MVDataEntry newEntry) throws LDAPException { + // Build the DN + DN dn = new DN(new RDN(keyAttr, newEntry.getKey()), baseDN); + + // Convert storage objects + Collection<Attribute> attributes = new ArrayList<Attribute>(); + for ( Map.Entry<String, String> entry : newEntry.getAllEntries() ) { + attributes.add(new Attribute(entry.getKey(), entry.getValue())); + } + Entry newLDAPEntry = new Entry(dn, attributes); + + // Add the entry + if ( dryRun ) { + // In dry-run mode, validate the entry + ArrayList<String> invalidReasons = new ArrayList<String>(5); + boolean valid = validator.entryIsValid(newLDAPEntry, invalidReasons); + if ( !valid ) throw new RuntimeException( + "Entry validator has failed to verify this entry :\n" + newLDAPEntry.toLDIFString() + + "Reasons are :\n" + invalidReasons); + } else { + // In real-run mode, insert the entry + try { + conn.add(newLDAPEntry); + } catch (LDAPException e) { + throw new LDAPException(e.getResultCode(), "Error while inserting this entry :\n" + newLDAPEntry.toLDIFString(), e); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void update(MVDataEntry updatedEntry, MVDataEntry originalEntry, Set<String> attrToUpdate) throws LDAPException, LDIFException { + // Build the DN + DN dn = new DN(new RDN(keyAttr, updatedEntry.getKey()), baseDN); + + // Convert storage objects + List<Modification> mods = new ArrayList<Modification>(); + for ( String attr : attrToUpdate ) { + Set<String> originalValues = originalEntry.getValues(attr); + Set<String> updatedValues = updatedEntry.getValues(attr); + + Modification modification = null; + + if ( updatedValues.isEmpty() ) { + modification = new Modification(ModificationType.DELETE, attr); + } else { + String[] updatedValuesArr = updatedValues.toArray(new String[0]); + + if ( originalValues.isEmpty() ) { + modification = new Modification(ModificationType.ADD, attr, updatedValuesArr); + } else { + modification = new Modification(ModificationType.REPLACE, attr, updatedValuesArr); + } + } + + mods.add(modification); + } + ModifyRequest modReq = new ModifyRequest(dn, mods); + + // Update the entry + if ( dryRun ) { + // Simulate originalEntry update + Collection<Attribute> attributes = new ArrayList<Attribute>(); + for ( Map.Entry<String, String> entry : originalEntry.getAllEntries() ) { + attributes.add(new Attribute(entry.getKey(), entry.getValue())); + } + Entry originalLDAPEntry = new Entry(dn, attributes); + + // Warning : Unboundid SDK is okay with mandatory attributes with value "" (empty string) + // OpenLDAP do not allow that empty strings in mandatory attributes. + // Empty strings are discarded by MVDataEntry.put() for now. + Entry modifiedLDAPEntry; + try { + modifiedLDAPEntry = Entry.applyModifications(originalLDAPEntry, false, mods); + } catch (LDAPException originalException) { + throw new RuntimeException("Entry update simulation has failed while running applyModifications()\n" + + "original entry : " + originalEntry + "\n" + + "wanted updated entry : " + updatedEntry + "\n" + + "modification request : " + modReq, + originalException); + } + ArrayList<String> invalidReasons = new ArrayList<String>(5); + boolean valid = validator.entryIsValid(modifiedLDAPEntry, invalidReasons); + if ( !valid ) throw new RuntimeException("Entry update simulation has failed while checking entryIsValid()\n" + + "modified entry : " + modifiedLDAPEntry.toLDIFString() + "\n" + + "reasons :" + invalidReasons); + } else { + // In real-run mode, update the entry + try { + conn.modify(modReq); + } catch (LDAPException originalException) { + throw new LDAPException(originalException.getResultCode(), + "Error while updating this entry :\n" + modReq.toLDIFString(), + originalException); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void delete(MVDataEntry existingEntry) throws LDAPException { + // Build the DN + DN dn = new DN(new RDN(keyAttr, existingEntry.getKey()), baseDN); + + // Delete the entry + try { + if ( dryRun ) { + //XXX : try to verify the entry existence in dry-run mode ? + } else { + conn.delete(new DeleteRequest(dn)); + } + } catch (LDAPException originalException) { + throw new LDAPException(originalException.getResultCode(), + "Error while deleting this dn : " + dn.toString(), + originalException); + } + } + +} |