Skip to content

Commit

Permalink
add option outputFormat + send lsc modifications as inputs and not ar…
Browse files Browse the repository at this point in the history
…guments (#193)
  • Loading branch information
David Coutadeur committed Oct 26, 2023
1 parent 6d51dfe commit 2f2ecb6
Show file tree
Hide file tree
Showing 12 changed files with 419 additions and 57 deletions.
82 changes: 60 additions & 22 deletions src/main/java/org/lsc/Hooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.ProcessBuilder;
import java.io.OutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.PrintWriter;
import org.lsc.utils.output.LdifLayout;
import com.fasterxml.jackson.databind.ObjectMapper; // For encoding object to JSON
import com.fasterxml.jackson.databind.ObjectWriter;



/**
* This object is managing posthook scripts
*/
Expand All @@ -63,31 +64,56 @@ public class Hooks {
*
* return nothing
*/
public final static void postSyncHook(final String hook, final LscModifications lm) {
public final static void postSyncHook(final String hook, final String outputFormat, final LscModifications lm) {

if( hook != null && ! hook.equals("") )
{
// Compute json modifications
String jsonModifications = null;

String format = "";
if( outputFormat.equals("json") ) {
format = "json";
}
else
{
format = "ldif";
}

// Compute json/ldif modifications
String modifications = null;

switch (lm.getOperation()) {
case CREATE_OBJECT:
jsonModifications = getJsonModifications(lm);
callHook("create", hook, lm.getMainIdentifier(), jsonModifications);
if( format.equals("json") ) {
modifications = getJsonModifications(lm);
}
else {
modifications = LdifLayout.format(lm);
}
callHook("create", hook, lm.getMainIdentifier(), format, modifications);
break;

case UPDATE_OBJECT:
jsonModifications = getJsonModifications(lm);
callHook("update", hook, lm.getMainIdentifier(), jsonModifications);
if( format.equals("json") ) {
modifications = getJsonModifications(lm);
}
else {
modifications = LdifLayout.format(lm);
}
callHook("update", hook, lm.getMainIdentifier(), format, modifications);
break;

case CHANGE_ID:
jsonModifications = getJsonModifications(lm);
callHook("changeId", hook, lm.getMainIdentifier(), jsonModifications);
if( format.equals("json") ) {
modifications = getJsonModifications(lm);
}
else {
modifications = LdifLayout.format(lm);
}
callHook("changeId", hook, lm.getMainIdentifier(), format, modifications);
break;

case DELETE_OBJECT:
callHook("delete", hook, lm.getMainIdentifier(), jsonModifications);
callHook("delete", hook, lm.getMainIdentifier(), format, modifications);
break;

default:
Expand All @@ -96,20 +122,32 @@ public final static void postSyncHook(final String hook, final LscModifications
}
}

/**
* Method calling the hook
*
* return nothing
*/
public final static void callHook( String operationType,
String hook,
String identifier,
String jsonModifications) {
String format,
String modifications) {

LOGGER.info("Calling {} posthook {} for {}", operationType, hook, identifier);
LOGGER.info("Calling {} posthook {} with format {} for {}", operationType, hook, format, identifier);
try {
if( jsonModifications != null ) {
if( modifications != null ) {
Process p = new ProcessBuilder(
hook,
identifier,
operationType,
jsonModifications)
operationType)
.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(
Expand All @@ -123,16 +161,16 @@ public final static void callHook( String operationType,
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
LOGGER.error("Error while calling {} posthook {} for {}: {}",
operationType,hook, identifier, sw.toString());
LOGGER.error("Error while calling {} posthook {} with format {} for {}: {}",
operationType, hook, format, identifier, sw.toString());
}
}

/**
* Method computing modifications as json
*
* @return modifications in a json String
*/
* Method computing modifications as json
*
* @return modifications in a json String
*/
public final static String getJsonModifications(final LscModifications lm) {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = "";
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/org/lsc/beans/syncoptions/ForceSyncOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ public String getCondition(LscModificationType operation) {
return DEFAULT_CONDITION;
}

public String getPostHookOutputFormat() {
return "";
}

public String getCreatePostHook() {
return "";
}
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/org/lsc/beans/syncoptions/ISyncOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,13 @@ public interface ISyncOptions {

String getCondition(LscModificationType operation);

/**
* Returns the posthook output format
*
* @return the posthook output format or "" if none is specified (default)
*/
String getPostHookOutputFormat();

/**
* Returns the posthook for a creation
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,13 @@ public String getCondition(LscModificationType operation) {
return result;
}

public String getPostHookOutputFormat() {
if (conf.getHooks() == null || conf.getHooks().getOutputFormat() == null) {
return "";
}
return conf.getHooks().getOutputFormat();
}

public String getCreatePostHook() {
if (conf.getHooks() == null || conf.getHooks().getCreatePostHook() == null) {
return "";
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/lsc/runnable/CleanEntryRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ public void run() {
if (task.getDestinationService().apply(lm)) {
// Retrieve posthook for the current operation
String hook = syncOptions.getDeletePostHook();
Hooks.postSyncHook(hook, lm);
String outputFormat = syncOptions.getPostHookOutputFormat();
Hooks.postSyncHook(hook, outputFormat, lm);
counter.incrementCountCompleted();
abstractSynchronize.logAction(lm, id, task.getName());
} else {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/lsc/runnable/SynchronizeEntryRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ public boolean run(IBean entry) {
if (task.getDestinationService().apply(lm)) {
// Retrieve posthook for the current operation
String hook = task.getSyncOptions().getPostHook(modificationType);
Hooks.postSyncHook(hook, lm);
String outputFormat = task.getSyncOptions().getPostHookOutputFormat();
Hooks.postSyncHook(hook, outputFormat, lm);
counter.incrementCountCompleted();
abstractSynchronize.logAction(lm, id, syncName);
return true;
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/schemas/lsc-core-2.2.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@

<xsd:complexType name="hooksType">
<xsd:sequence>
<xsd:element name="outputFormat" type="xsd:string" minOccurs="0" />
<xsd:element name="createPostHook" type="xsd:string" minOccurs="0" />
<xsd:element name="updatePostHook" type="xsd:string" minOccurs="0" />
<xsd:element name="deletePostHook" type="xsd:string" minOccurs="0" />
Expand Down
134 changes: 118 additions & 16 deletions src/test/java/org/lsc/Ldap2LdapHookSyncTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@
*/
package org.lsc;

import com.fasterxml.jackson.databind.ObjectMapper; // For encoding object to JSON
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import java.io.StringWriter;
import java.io.PrintWriter;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
Expand All @@ -55,7 +64,6 @@
import org.junit.Before;
import org.junit.Test;
import org.lsc.configuration.LscConfiguration;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand All @@ -81,29 +89,78 @@ public void setup() {
}

@Test
public final void testLdap2LdapHookSyncTest() throws Exception {
public final void testLdap2LdapJSONHookSyncTest() throws Exception {

// Declare the tasks to launch in the correct order
List<String> sync_tasks = Arrays.asList("ldap2ldapJSONHookTestCreate", "ldap2ldapJSONHookTestUpdate");
List<String> clean_tasks = Arrays.asList("ldap2ldapJSONHookTestDelete");

// perform the sync
launchSyncCleanTask(sync_tasks, false, true, false);
launchSyncCleanTask(clean_tasks, false, false, true);

// check the results of the synchronization
reloadJndiConnections();

ObjectMapper mapperCreatedEntry = new ObjectMapper();
JsonNode expectedCreatedEntry = mapperCreatedEntry.readTree("[ { \"attributeName\" : \"objectClass\", \"values\" : [ \"inetOrgPerson\", \"person\", \"top\" ], \"operation\" : \"ADD_VALUES\" }, { \"attributeName\" : \"cn\", \"values\" : [ \"CN0001-hook\" ], \"operation\" : \"ADD_VALUES\" }, { \"attributeName\" : \"sn\", \"values\" : [ \"CN0001-hook\" ], \"operation\" : \"ADD_VALUES\" } ]");

checkJSONSyncResults("create", expectedCreatedEntry);

ObjectMapper mapperUpdatedEntry = new ObjectMapper();
JsonNode expectedUpdatedEntry = mapperUpdatedEntry.readTree("[ { \"attributeName\" : \"description\", \"values\" : [ \"CN0001-hook\" ], \"operation\" : \"REPLACE_VALUES\" } ]");

checkJSONSyncResults("update", expectedUpdatedEntry);

checkJSONSyncResults("delete", null);
}

@Test
public final void testLdap2LdapLDIFHookSyncTest() throws Exception {

// Declare the tasks to launch in the correct order
List<String> sync_tasks = Arrays.asList("ldap2ldapHookTestCreate", "ldap2ldapHookTestUpdate");
List<String> clean_tasks = Arrays.asList("ldap2ldapHookTestDelete");
List<String> sync_tasks = Arrays.asList("ldap2ldapLDIFHookTestCreate", "ldap2ldapLDIFHookTestUpdate");
List<String> clean_tasks = Arrays.asList("ldap2ldapLDIFHookTestDelete");

// perform the sync
launchSyncCleanTask(sync_tasks, false, true, false);
launchSyncCleanTask(clean_tasks, false, false, true);

// check the results of the synchronization
reloadJndiConnections();
checkSyncResults();

List<String> expectedCreatedEntry = Arrays.asList(
"dn: cn=CN0001-hook,ou=ldap2ldap2TestTaskDst,ou=Test Data,dc=lsc-project,dc=org",
"changetype: add",
"objectClass: inetOrgPerson",
"objectClass: person",
"objectClass: top",
"cn: CN0001-hook",
"sn: CN0001-hook"
);

checkLDIFSyncResults("create", expectedCreatedEntry);

List<String> expectedUpdatedEntry = Arrays.asList(
"dn: cn=CN0001-hook,ou=ldap2ldap2TestTaskDst,ou=Test Data,dc=lsc-project,dc=org",
"changetype: modify",
"replace: description",
"description: CN0001-hook"
);

checkLDIFSyncResults("update", expectedUpdatedEntry);

checkLDIFSyncResults("delete", new ArrayList<String>());
}

/*
* Read hook log file to check passed arguments
* Read hook log file to check passed arguments and modification passed as input
*/
private final void checkSyncResults() throws Exception {
private final void checkLDIFSyncResults(String operation, List<String> expectedEntry) throws Exception {

List<String> hookResults = new ArrayList<String>();
try {
File hookFile = new File("hook.log");
File hookFile = new File("hook-ldif-" + operation + ".log");
Scanner hookReader = new Scanner(hookFile);

while (hookReader.hasNextLine()) {
Expand All @@ -113,16 +170,61 @@ private final void checkSyncResults() throws Exception {
hookReader.close();
hookFile.delete(); // remove hook log
} catch (Exception e) {
fail("Error while reading hook.log");
fail("Error while reading hook-ldif-" + operation + ".log");
}
assertEquals(hookResults.get(0), "cn=CN0001-hook,ou=ldap2ldap2TestTaskDst,ou=Test Data,dc=lsc-project,dc=org");
assertEquals(hookResults.get(1), "create");
assertTrue("Hook logs: ADD_VALUES not found in created entry", hookResults.get(2).matches("^.*ADD_VALUES.*$"));
assertEquals(hookResults.get(3), "cn=CN0001-hook,ou=ldap2ldap2TestTaskDst,ou=Test Data,dc=lsc-project,dc=org");
assertEquals(hookResults.get(4), "update");
assertTrue("Hook logs: REPLACE_VALUES not found in updated entry", hookResults.get(5).matches("^.*REPLACE_VALUES.*$"));
assertEquals(hookResults.get(6), "cn=CN0001-hook,ou=ldap2ldap2TestTaskDst,ou=Test Data,dc=lsc-project,dc=org");
assertEquals(hookResults.get(7), "delete");
assertEquals(hookResults.get(1), operation);

if(operation != "delete") {
// Make sure all attributes in expectedEntry are present in the hook file
List<String> entry = new ArrayList(hookResults.subList(3, (hookResults.size()-1)));
for (String attr : expectedEntry) {
assertTrue("Attribute " + attr + " not found in " + operation + " entry " + entry.toString(), entry.contains(attr));
}
}

}

/*
* Read hook log file to check passed arguments and modification passed as input
*/
private final void checkJSONSyncResults(String operation, JsonNode expectedEntry) throws Exception {

List<String> hookResults = new ArrayList<String>();
try {
File hookFile = new File("hook-json-" + operation + ".log");
Scanner hookReader = new Scanner(hookFile);

while (hookReader.hasNextLine()) {
String data = hookReader.nextLine();
hookResults.add(data);
}
hookReader.close();
hookFile.delete(); // remove hook log
} catch (Exception e) {
fail("Error while reading hook-json-" + operation + ".log");
}
assertEquals(hookResults.get(0), "cn=CN0001-hook,ou=ldap2ldap2TestTaskDst,ou=Test Data,dc=lsc-project,dc=org");
assertEquals(hookResults.get(1), operation);

if(operation != "delete") {
// Make sure all attributes in expectedEntry are present in the hook file
String entry = String.join("", new ArrayList(hookResults.subList(2, hookResults.size())));

ObjectMapper mapper = new ObjectMapper();
JsonFactory factory = mapper.getJsonFactory();
JsonParser jp = factory.createJsonParser(entry);
try {
JsonNode hookOperation = mapper.readTree(jp);
assertEquals(hookOperation, expectedEntry);
}
catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
fail("Error while decoding operation in hook-json-" + operation + ".log as JSON:" + sw.toString());
}
}

}

Expand Down
Loading

0 comments on commit 2f2ecb6

Please sign in to comment.