Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ private Constants() {

public static final String PARAMETER_ELEMENT_TYPE_NAME = "parameter";
public static final String PROPERTY_ELEMENT_TYPE_NAME = "property";
public static final String SECURE_EXTENSION_DESCRIPTOR_ID = "__mta.secure";
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
package org.cloudfoundry.multiapps.mta.builders;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.cloudfoundry.multiapps.common.ContentException;
import org.cloudfoundry.multiapps.mta.Constants;
import org.cloudfoundry.multiapps.mta.Messages;
import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor;
import org.cloudfoundry.multiapps.mta.model.Descriptor;
import org.cloudfoundry.multiapps.mta.model.ExtensionDescriptor;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class ExtensionDescriptorChainBuilder {

private final boolean isStrict;

private ExtensionDescriptor secureExtensionDescriptor;

public ExtensionDescriptorChainBuilder() {
this(true);
}
Expand All @@ -24,8 +28,14 @@ public ExtensionDescriptorChainBuilder(boolean isStrict) {
this.isStrict = isStrict;
}

private boolean isSecureDescriptor(ExtensionDescriptor extensionDescriptor) {
return extensionDescriptor.getId()
.equals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID);
}

public List<ExtensionDescriptor> build(DeploymentDescriptor deploymentDescriptor, List<ExtensionDescriptor> extensionDescriptors)
throws ContentException {
saveSecureExtensionDescriptor(extensionDescriptors);
Map<String, ExtensionDescriptor> extensionDescriptorsPerParent = getExtensionDescriptorsPerParent(extensionDescriptors);
return build(deploymentDescriptor, extensionDescriptorsPerParent);
}
Expand All @@ -34,40 +44,70 @@ private List<ExtensionDescriptor> build(DeploymentDescriptor deploymentDescripto
Map<String, ExtensionDescriptor> extensionDescriptorsPerParent) {
List<ExtensionDescriptor> chain = new ArrayList<>();
Descriptor currentDescriptor = deploymentDescriptor;

while (currentDescriptor != null) {
ExtensionDescriptor nextDescriptor = extensionDescriptorsPerParent.remove(currentDescriptor.getId());
CollectionUtils.addIgnoreNull(chain, nextDescriptor);
currentDescriptor = nextDescriptor;
}

CollectionUtils.addIgnoreNull(chain, this.secureExtensionDescriptor);

if (!extensionDescriptorsPerParent.isEmpty() && isStrict) {
throw new ContentException(Messages.CANNOT_BUILD_EXTENSION_DESCRIPTOR_CHAIN_BECAUSE_DESCRIPTORS_0_HAVE_AN_UNKNOWN_PARENT,
String.join(",", Descriptor.getIds(extensionDescriptorsPerParent.values())));
}

return chain;
}

private Map<String, ExtensionDescriptor> getExtensionDescriptorsPerParent(List<ExtensionDescriptor> extensionDescriptors) {
Map<String, List<ExtensionDescriptor>> extensionDescriptorsPerParent = extensionDescriptors.stream()
.collect(Collectors.groupingBy(ExtensionDescriptor::getParentId));
.collect(Collectors.groupingBy(
ExtensionDescriptor::getParentId));
validateSingleExtensionDescriptorPerParent(extensionDescriptorsPerParent);
return prune(extensionDescriptorsPerParent);
}

private Map<String, ExtensionDescriptor> prune(Map<String, List<ExtensionDescriptor>> extensionDescriptorsPerParent) {
validateSingleExtensionDescriptorPerParent(extensionDescriptorsPerParent);
return extensionDescriptorsPerParent.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue()
.get(0)));
Map<String, ExtensionDescriptor> resultMap = new LinkedHashMap<>();

for (Map.Entry<String, List<ExtensionDescriptor>> entry : extensionDescriptorsPerParent.entrySet()) {
for (ExtensionDescriptor currentExtensionDescriptorForParent : entry.getValue()) {
if (!isSecureDescriptor(currentExtensionDescriptorForParent)) {
resultMap.put(entry.getKey(), currentExtensionDescriptorForParent);
break;
}
}
}
return resultMap;
}

private void validateSingleExtensionDescriptorPerParent(Map<String, List<ExtensionDescriptor>> extensionDescriptorsPerParent) {
for (Map.Entry<String, List<ExtensionDescriptor>> extensionDescriptorsForParent : extensionDescriptorsPerParent.entrySet()) {
String parent = extensionDescriptorsForParent.getKey();
List<ExtensionDescriptor> extensionDescriptors = extensionDescriptorsForParent.getValue();
if (extensionDescriptors.size() > 1 && isStrict) {
long nonSecureCountOfExtensionDescriptors = extensionDescriptors.stream()
.filter(descriptor -> !descriptor.getId()
.equals(
Constants.SECURE_EXTENSION_DESCRIPTOR_ID))
.count();
if (nonSecureCountOfExtensionDescriptors > 1 && isStrict) {
throw new ContentException(Messages.MULTIPLE_EXTENSION_DESCRIPTORS_EXTEND_THE_PARENT_0, parent);
}
}
}

}
private void saveSecureExtensionDescriptor(List<ExtensionDescriptor> extensionDescriptors) {
ExtensionDescriptor lastSecureExtensionDescriptor = null;

for (ExtensionDescriptor extensionDescriptor : extensionDescriptors) {
if (isSecureDescriptor(extensionDescriptor)) {
lastSecureExtensionDescriptor = extensionDescriptor;
}
}

this.secureExtensionDescriptor = lastSecureExtensionDescriptor;
}

}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package org.cloudfoundry.multiapps.mta.model;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.ObjectUtils;
import org.cloudfoundry.multiapps.common.util.yaml.YamlElement;
import org.cloudfoundry.multiapps.mta.parsers.v3.ExtensionHookParser;

public class ExtensionHook extends VersionedEntity implements VisitableElement, NamedElement {
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class ExtensionHook extends VersionedEntity implements VisitableElement, NamedElement, ParametersContainer {

// Required by Jackson.
protected ExtensionHook() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package org.cloudfoundry.multiapps.mta.builders;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.cloudfoundry.multiapps.common.ContentException;
import org.cloudfoundry.multiapps.mta.Constants;
import org.cloudfoundry.multiapps.mta.Messages;
import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor;
import org.cloudfoundry.multiapps.mta.model.ExtensionDescriptor;
import org.junit.jupiter.api.Test;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.cloudfoundry.multiapps.common.ContentException;
import org.cloudfoundry.multiapps.mta.Messages;
import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor;
import org.cloudfoundry.multiapps.mta.model.ExtensionDescriptor;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;

class ExtensionDescriptorChainBuilderTest {

Expand Down Expand Up @@ -50,8 +52,9 @@ void testBuildWithDescriptorWithUnknownParent() {
ContentException contentException = assertThrows(ContentException.class,
() -> extensionDescriptorChainBuilder.build(deploymentDescriptor,
extensionDescriptors));
String expectedMessage = MessageFormat.format(Messages.CANNOT_BUILD_EXTENSION_DESCRIPTOR_CHAIN_BECAUSE_DESCRIPTORS_0_HAVE_AN_UNKNOWN_PARENT,
extensionDescriptor2.getId());
String expectedMessage = MessageFormat.format(
Messages.CANNOT_BUILD_EXTENSION_DESCRIPTOR_CHAIN_BECAUSE_DESCRIPTORS_0_HAVE_AN_UNKNOWN_PARENT,
extensionDescriptor2.getId());

assertEquals(expectedMessage, contentException.getMessage());
}
Expand Down Expand Up @@ -102,6 +105,96 @@ void testLaxBuildWithMultipleDescriptorsExtendingTheSameParent() {
assertEquals(Collections.emptyList(), extensionDescriptorChain);
}

@Test
void testBuildWithNormalAndSecureExtensionDescriptorsWhereSecureIsLastInTheChain() {
DeploymentDescriptor deploymentDescriptor = buildDeploymentDescriptor(FOO_ID);
ExtensionDescriptor normalExtensionDescriptor = buildExtensionDescriptor(BAR_ID, FOO_ID);
ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID);

List<ExtensionDescriptor> extensionDescriptors = List.of(secureExtensionDescriptor, normalExtensionDescriptor);

ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder();
List<ExtensionDescriptor> extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor,
extensionDescriptors);

assertEquals(List.of(normalExtensionDescriptor, secureExtensionDescriptor), extensionDescriptorChain);
}

@Test
void testBuildWithOnlySecureExtensionDescriptor() {
DeploymentDescriptor deploymentDescriptor = buildDeploymentDescriptor(FOO_ID);
ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID);

List<ExtensionDescriptor> extensionDescriptors = Collections.singletonList(secureExtensionDescriptor);

ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder();
List<ExtensionDescriptor> extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor,
extensionDescriptors);

assertEquals(Collections.singletonList(secureExtensionDescriptor), extensionDescriptorChain);
}

@Test
void testBuildWhenSecureExtensionDescriptorIsExtendedStrictMode() {
DeploymentDescriptor deploymentDescriptor = buildDeploymentDescriptor(FOO_ID);
ExtensionDescriptor firstExtensionDescriptor = buildExtensionDescriptor(BAR_ID, FOO_ID);
ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(BAR_ID);
ExtensionDescriptor extensionDescriptorAfterSecureOne = buildExtensionDescriptor(QUX_ID, Constants.SECURE_EXTENSION_DESCRIPTOR_ID);

List<ExtensionDescriptor> extensionDescriptors = List.of(extensionDescriptorAfterSecureOne, firstExtensionDescriptor,
secureExtensionDescriptor);

ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(true);

ContentException contentException = assertThrows(ContentException.class,
() -> extensionDescriptorChainBuilder.build(deploymentDescriptor,
extensionDescriptors));

String expectedMessage = MessageFormat.format(
Messages.CANNOT_BUILD_EXTENSION_DESCRIPTOR_CHAIN_BECAUSE_DESCRIPTORS_0_HAVE_AN_UNKNOWN_PARENT,
extensionDescriptorAfterSecureOne.getId()
);
assertEquals(expectedMessage, contentException.getMessage());
}

@Test
void testBuildWhenSecureExtensionDescriptorIsExtendedNonStrictMode() {
DeploymentDescriptor deploymentDescriptor = buildDeploymentDescriptor(FOO_ID);
ExtensionDescriptor firstExtensionDescriptor = buildExtensionDescriptor(BAR_ID, FOO_ID);
ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(BAR_ID);
ExtensionDescriptor extensionDescriptorAfterSecureOne = buildExtensionDescriptor(QUX_ID, Constants.SECURE_EXTENSION_DESCRIPTOR_ID);

List<ExtensionDescriptor> extensionDescriptors = List.of(extensionDescriptorAfterSecureOne, secureExtensionDescriptor,
firstExtensionDescriptor);

ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(false);
List<ExtensionDescriptor> extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor,
extensionDescriptors);

assertEquals(List.of(firstExtensionDescriptor, secureExtensionDescriptor), extensionDescriptorChain);
}

@Test
void testBuildWhenTwoSecureExtensionDescriptorsWhereOnlyTheLastOneGetsAppended() {
DeploymentDescriptor deploymentDescriptor = buildDeploymentDescriptor(FOO_ID);
ExtensionDescriptor firstExtensionDescriptor = buildExtensionDescriptor(BAR_ID, FOO_ID);
ExtensionDescriptor firstSecureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID);
ExtensionDescriptor secondSecureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID);

List<ExtensionDescriptor> extensionDescriptors = List.of(firstSecureExtensionDescriptor, firstExtensionDescriptor,
secondSecureExtensionDescriptor);

ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder();
List<ExtensionDescriptor> extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor,
extensionDescriptors);

assertEquals(2, extensionDescriptorChain.size());
assertEquals(firstExtensionDescriptor, extensionDescriptorChain.get(0));
assertEquals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID, extensionDescriptorChain.get(1)
.getId());
assertSame(secondSecureExtensionDescriptor, extensionDescriptorChain.get(1));
}

private DeploymentDescriptor buildDeploymentDescriptor(String id) {
return DeploymentDescriptor.createV2()
.setId(id);
Expand All @@ -113,4 +206,10 @@ private ExtensionDescriptor buildExtensionDescriptor(String id, String parentId)
.setId(id);
}

private ExtensionDescriptor buildSecureExtensionDescriptor(String parentId) {
return ExtensionDescriptor.createV2()
.setParentId(parentId)
.setId(Constants.SECURE_EXTENSION_DESCRIPTOR_ID);
}

}
Loading