Skip to content

Commit

Permalink
make DittoHeadersValidator.validate async, add unit test
Browse files Browse the repository at this point in the history
Signed-off-by: Johannes Schneider <johannes.schneider@bosch.io>
  • Loading branch information
jokraehe committed Aug 25, 2022
1 parent 8dc4484 commit 156f17e
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 21 deletions.
Expand Up @@ -160,7 +160,7 @@ private Stream<MappingOutcome<MappedInboundExternalMessage>> runMapper(final Mes
final Signal<?> signal =
timer.inboundProtocol(() -> protocolAdapter.fromAdaptable(adaptable));
final DittoHeaders dittoHeaders = signal.getDittoHeaders();
dittoHeadersSizeValidator.validate(dittoHeaders);
dittoHeadersSizeValidator.validate(dittoHeaders).toCompletableFuture().join();
final DittoHeaders headersWithMapper = dittoHeaders.toBuilder()
.inboundPayloadMapper(mapper.getId())
.putHeaders(additionalInboundHeaders)
Expand Down
Expand Up @@ -14,6 +14,9 @@

import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

import javax.annotation.concurrent.Immutable;

import org.eclipse.ditto.base.model.exceptions.DittoHeadersTooLargeException;
Expand All @@ -23,7 +26,7 @@
import com.typesafe.config.Config;

/**
* Provider for a {@link DittoHeadersValidator}.
* Default implementation for {@link DittoHeadersValidator}.
*/
@Immutable
public final class DefaultDittoHeadersValidator implements DittoHeadersValidator {
Expand All @@ -41,21 +44,21 @@ public DefaultDittoHeadersValidator(final ActorSystem actorSystem, final Config
}

@Override
public void validate(DittoHeaders dittoHeaders) {
public CompletionStage<DittoHeaders> validate(DittoHeaders dittoHeaders) {
checkNotNull(dittoHeaders, "dittoHeaders");
validateAuthorizationContext(dittoHeaders);
if (dittoHeaders.isEntriesSizeGreaterThan(maxBytes)) {
throw DittoHeadersTooLargeException.newSizeLimitBuilder(maxBytes)
.dittoHeaders(dittoHeaders)
.build();
try {
validateAuthorizationContext(dittoHeaders);
if (dittoHeaders.isEntriesSizeGreaterThan(maxBytes)) {
throw DittoHeadersTooLargeException.newSizeLimitBuilder(maxBytes)
.dittoHeaders(dittoHeaders)
.build();
}
return CompletableFuture.completedStage(dittoHeaders);
} catch (DittoHeadersTooLargeException e) {
return CompletableFuture.failedStage(e);
}
}

@Override
public DittoHeaders truncate(DittoHeaders dittoHeaders) {
return dittoHeaders.truncate(maxBytes);
}

private void validateAuthorizationContext(final DittoHeaders dittoHeaders) {
final int authSubjectsCount = dittoHeaders.getAuthorizationContext().getSize();
if (authSubjectsCount > maxAuthSubjects) {
Expand All @@ -65,4 +68,9 @@ private void validateAuthorizationContext(final DittoHeaders dittoHeaders) {
}
}

@Override
public DittoHeaders truncate(DittoHeaders dittoHeaders) {
return dittoHeaders.truncate(maxBytes);
}

}
Expand Up @@ -14,6 +14,8 @@

import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;

import java.util.concurrent.CompletionStage;

import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.internal.utils.extension.DittoExtensionIds;
import org.eclipse.ditto.internal.utils.extension.DittoExtensionPoint;
Expand All @@ -30,9 +32,11 @@ public interface DittoHeadersValidator extends DittoExtensionPoint {
* Validates {@code dittoHeaders} against limits defined in the extension configuration.
*
* @param dittoHeaders the headers to validate.
* @throws org.eclipse.ditto.base.model.exceptions.DittoHeadersTooLargeException if {@code dittoHeaders} are not valid.
* @return a completion stage which completes successfully with the valid headers. Raises a
* {@link org.eclipse.ditto.base.model.exceptions.DittoHeadersTooLargeException} if {@code dittoHeaders} are not
* valid.
*/
void validate(DittoHeaders dittoHeaders);
CompletionStage<DittoHeaders> validate(DittoHeaders dittoHeaders);

/**
* Truncates {@code dittoHeaders} to validate against the configured limits.
Expand Down
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.edge.service.headers;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nullable;

import org.eclipse.ditto.base.model.auth.AuthorizationContext;
import org.eclipse.ditto.base.model.auth.AuthorizationSubject;
import org.eclipse.ditto.base.model.auth.DittoAuthorizationContextType;
import org.eclipse.ditto.base.model.exceptions.DittoHeadersTooLargeException;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.internal.utils.config.ScopedConfig;

import akka.actor.ActorSystem;
import akka.testkit.javadsl.TestKit;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

/**
* Unit test for {@link DefaultDittoHeadersValidator}.
*/
public final class DefaultDittoHeadersValidatorTest {

@Nullable
private static ActorSystem actorSystem;
@Nullable
private static Config extensionConfig;

@BeforeClass
public static void init() {
actorSystem = ActorSystem.create("AkkaTestSystem", ConfigFactory.load("test"));
extensionConfig = ScopedConfig.dittoExtension(actorSystem.settings().config())
.getConfig("ditto-headers-validator.extension-config");
}

@AfterClass
public static void tearDown() {
if (actorSystem != null) {
TestKit.shutdownActorSystem(actorSystem);
}
}

@Test
public void validationFailsForTooManyAuthSubjects() {
assert actorSystem != null;
assert extensionConfig != null;

final var underTest = new DefaultDittoHeadersValidator(actorSystem, extensionConfig);

final var authorizationContext = AuthorizationContext.newInstance(
DittoAuthorizationContextType.PRE_AUTHENTICATED_HTTP,
AuthorizationSubject.newInstance("ditto:ditto1"),
AuthorizationSubject.newInstance("ditto:ditto2"));
final var dittoHeaders = DittoHeaders.newBuilder()
.authorizationContext(authorizationContext)
.build();

final var validationResult = underTest.validate(dittoHeaders);

assertThat(validationResult).failsWithin(1, TimeUnit.SECONDS)
.withThrowableOfType(ExecutionException.class)
.withCauseInstanceOf(DittoHeadersTooLargeException.class);
}

}
7 changes: 7 additions & 0 deletions edge/service/src/test/resources/test.conf
Expand Up @@ -16,4 +16,11 @@ ditto.extensions {
"org.eclipse.ditto.edge.service.dispatching.EdgeCommandForwarderActorTestSignalTransformer"
]
}
ditto-headers-validator = {
extension-class = org.eclipse.ditto.edge.service.headers.DefaultDittoHeadersValidator
extension-config {
max-bytes = 1k
max-auth-subjects = 1
}
}
}
Expand Up @@ -227,12 +227,7 @@ CompletionStage<DittoHeaders> build(final CustomHeadersHandler.RequestType reque
requestType,
dittoDefaultHeaders);

result = result.thenApply(dittoHeaders -> {
dittoHeadersValidator.validate(dittoHeaders);
return dittoHeaders;
});

return result;
return result.thenCompose(dittoHeadersValidator::validate);
}

}
Expand Down

0 comments on commit 156f17e

Please sign in to comment.