Skip to content

Commit

Permalink
add a create/update/changeId/delete posthook (#193) (#242)
Browse files Browse the repository at this point in the history
add a create/update/changeId/delete posthook (#193)

---------

Co-authored-by: David Coutadeur <[email protected]>
  • Loading branch information
davidcoutadeur and David Coutadeur authored Dec 11, 2023
1 parent 9943392 commit 505580e
Show file tree
Hide file tree
Showing 13 changed files with 958 additions and 2 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
<staging.dir>${project.build.directory}/staging</staging.dir>
<jmockit-version>1.5</jmockit-version>
<graalvm.version>22.3.1</graalvm.version>
<jackson.version>2.9.6</jackson.version>
<compiler.dir>${project.build.directory}/compiler</compiler.dir>
</properties>

Expand Down Expand Up @@ -872,6 +873,11 @@
<artifactId>xml-apis</artifactId>
<version>1.4.01</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>

<distributionManagement>
Expand Down
148 changes: 148 additions & 0 deletions src/main/java/org/lsc/Hooks.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
****************************************************************************
* Ldap Synchronization Connector provides tools to synchronize
* electronic identities from a list of data sources including
* any database with a JDBC connector, another LDAP directory,
* flat files...
*
* ==LICENSE NOTICE==
*
* Copyright (c) 2008 - 2011 LSC Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*    * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*     * Neither the name of the LSC Project nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ==LICENSE NOTICE==
*
* (c) 2008 - 2011 LSC Project
****************************************************************************
*/
package org.lsc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.ProcessBuilder;
import java.io.OutputStream;
import java.io.IOException;
import java.util.Optional;
import org.lsc.utils.output.LdifLayout;
import org.lsc.beans.syncoptions.ISyncOptions.OutputFormat;
import com.fasterxml.jackson.databind.ObjectMapper; // For encoding object to JSON
import com.fasterxml.jackson.databind.ObjectWriter;


/**
* This object is managing posthook scripts
*/
public class Hooks {

static final Logger LOGGER = LoggerFactory.getLogger(Hooks.class);

private static final ObjectMapper Mapper = new ObjectMapper();
/**
* Method calling a postSyncHook if necessary
*
* return nothing
*/
public final void postSyncHook(final Optional<String> hook, final OutputFormat outputFormat, final LscModifications lm) {

hook.ifPresent( h -> callHook(lm.getOperation(), h, lm.getMainIdentifier(), outputFormat, lm));
}

public final static void callHook( LscModificationType operationType,
String hook,
String identifier,
OutputFormat outputFormat,
LscModifications lm) {

LOGGER.info("Calling {} posthook {} with format {} for {}",
operationType.getDescription(),
hook,
outputFormat.toString(),
identifier);

String modifications = null;
// Compute modifications only in a create / update / changeid operation
if( operationType != LscModificationType.DELETE_OBJECT)
{
if( outputFormat == OutputFormat.JSON ) {
modifications = getJsonModifications(lm);
}
else {
modifications = LdifLayout.format(lm);
}
}

try {
if( modifications != null ) {
Process p = new ProcessBuilder(
hook,
identifier,
operationType.getDescription())
.start();

// sends ldif modifications to stdin of hook script
OutputStream stdin = p.getOutputStream();
stdin.write(modifications.getBytes());
stdin.write("\n".getBytes());
stdin.flush();
stdin.close();
}
else {
Process p = new ProcessBuilder(
hook,
identifier,
operationType.getDescription())
.start();
}
}
catch(IOException e) {
LOGGER.error("Error while calling {} posthook {} with format {} for {}",
operationType.getDescription(),
hook,
outputFormat.toString(),
identifier,
e);
}
}

/**
* Method computing modifications as json
*
* @return modifications in a json String
*/
public final static String getJsonModifications(final LscModifications lm) {
ObjectWriter ow = Mapper.writer().withDefaultPrettyPrinter();
String json = "";
try {
json = ow.writeValueAsString(lm.getLscAttributeModifications());
}
catch(Exception e) {
LOGGER.error("Error while encoding LSC modifications to json", e);
}
return json;
}

}
25 changes: 25 additions & 0 deletions src/main/java/org/lsc/beans/syncoptions/ForceSyncOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@

import java.util.List;
import java.util.Set;
import java.util.Optional;

import org.lsc.LscModificationType;
import org.lsc.configuration.PolicyType;
Expand Down Expand Up @@ -128,6 +129,30 @@ public String getCondition(LscModificationType operation) {
return DEFAULT_CONDITION;
}

public OutputFormat getPostHookOutputFormat() {
return OutputFormat.LDIF;
}

public Optional<String> getCreatePostHook() {
return Optional.empty();
}

public Optional<String> getDeletePostHook() {
return Optional.empty();
}

public Optional<String> getUpdatePostHook() {
return Optional.empty();
}

public Optional<String> getChangeIdPostHook() {
return Optional.empty();
}

public Optional<String> getPostHook(LscModificationType operation) {
return Optional.empty();
}

public String getDn() {
return null;//((Ldap)task.getDestinationService()).getDn();
}
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/org/lsc/beans/syncoptions/ISyncOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@

import java.util.List;
import java.util.Set;
import java.util.Optional;

import org.lsc.LscModificationType;
import org.lsc.configuration.PolicyType;
Expand All @@ -60,6 +61,9 @@ public interface ISyncOptions {

/** default condition if none is given */
public static final String DEFAULT_CONDITION = "true";

/** list of output formats */
public enum OutputFormat { LDIF, JSON };

/**
* Initialize the synchronization options policy.
Expand Down Expand Up @@ -153,6 +157,44 @@ public interface ISyncOptions {

String getCondition(LscModificationType operation);

/**
* Returns the posthook output format
*
* @return the posthook output format (default = OutputFormat.LDIF)
*/
OutputFormat getPostHookOutputFormat();

/**
* Returns the posthook for a creation
*
* @return the posthook or "" if none is specified (default)
*/
Optional<String> getCreatePostHook();

/**
* Returns the posthook for an update
*
* @return the posthook or "" if none is specified (default)
*/
Optional<String> getUpdatePostHook();

/**
* Returns the posthook for a delete
*
* @return the posthook or "" if none is specified (default)
*/
Optional<String> getDeletePostHook();

/**
* Returns the posthook for a id change
*
* @return the posthook or "" if none is specified (default)
*/
Optional<String> getChangeIdPostHook();


Optional<String> getPostHook(LscModificationType operation);

/**
* Return the expression used to infer the new object DN
* @return the new object dn pattern
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Optional;

import org.lsc.LscModificationType;
import org.lsc.configuration.DatasetType;
Expand Down Expand Up @@ -196,6 +197,53 @@ public String getCondition(LscModificationType operation) {
}
return result;
}

public OutputFormat getPostHookOutputFormat() {
// default: returns LDIF format
if (conf.getHooks() == null || conf.getHooks().getOutputFormat() == null) {
return OutputFormat.LDIF;
}
switch(conf.getHooks().getOutputFormat()){
case "json":
return OutputFormat.JSON;
default:
return OutputFormat.LDIF;
}
}

public Optional<String> getCreatePostHook() {
Optional<String> hook = Optional.ofNullable(conf.getHooks().getCreatePostHook()).filter(s -> !s.isEmpty());
return hook;
}

public Optional<String> getDeletePostHook() {
Optional<String> hook = Optional.ofNullable(conf.getHooks().getDeletePostHook()).filter(s -> !s.isEmpty());
return hook;
}

public Optional<String> getUpdatePostHook() {
Optional<String> hook = Optional.ofNullable(conf.getHooks().getUpdatePostHook()).filter(s -> !s.isEmpty());
return hook;
}

public Optional<String> getChangeIdPostHook() {
Optional<String> hook = Optional.ofNullable(conf.getHooks().getChangeIdPostHook()).filter(s -> !s.isEmpty());
return hook;
}

public Optional<String> getPostHook(LscModificationType operation) {
switch (operation) {
case CREATE_OBJECT:
return this.getCreatePostHook();
case UPDATE_OBJECT:
return this.getUpdatePostHook();
case DELETE_OBJECT:
return this.getDeletePostHook();
case CHANGE_ID:
return this.getChangeIdPostHook();
}
return Optional.empty();
}

public String getDelimiter(String name) {
DatasetType dataset = LscConfiguration.getDataset(conf, name);
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/lsc/configuration/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/AuditType.java
/AuditsType.java
/ConditionsType.java
/HooksType.java
/ConnectionType.java
/ConnectionsType.java
/CsvAuditType.java
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/org/lsc/runnable/CleanEntryRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;

import javax.naming.CommunicationException;

Expand All @@ -23,6 +24,8 @@
import org.lsc.utils.ScriptingEvaluator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.lsc.Hooks;
import org.lsc.beans.syncoptions.ISyncOptions.OutputFormat;

/**
* @author sfroger
Expand All @@ -31,11 +34,13 @@ public class CleanEntryRunner extends AbstractEntryRunner {

static final Logger LOGGER = LoggerFactory.getLogger(CleanEntryRunner.class);

private Hooks hooks;

public CleanEntryRunner(final Task task, InfoCounter counter,
AbstractSynchronize abstractSynchronize,
Entry<String, LscDatasets> id) {
super(task, counter, abstractSynchronize, id);
this.hooks = new Hooks();
}

@Override
Expand Down Expand Up @@ -108,6 +113,10 @@ public void run() {
// if we got here, we have a modification to apply - let's
// do it!
if (task.getDestinationService().apply(lm)) {
// Retrieve posthook for the current operation
hooks.postSyncHook( syncOptions.getDeletePostHook(),
syncOptions.getPostHookOutputFormat(),
lm);
counter.incrementCountCompleted();
abstractSynchronize.logAction(lm, id, task.getName());
} else {
Expand Down
Loading

0 comments on commit 505580e

Please sign in to comment.