Search in sources :

Example 31 with ResourceIdentifier

use of org.eclipse.hono.util.ResourceIdentifier in project hono by eclipse.

the class FileBasedAuthenticationServiceTest method testVerifyPlainAddsAuthoritiesForOperations.

/**
 * Verifies that the token issued by the service on successful verification
 * of credentials contains the user's authorities for executing operations.
 *
 * @param ctx The vert.x test context.
 */
@Test
public void testVerifyPlainAddsAuthoritiesForOperations(final VertxTestContext ctx) {
    final ResourceIdentifier registration = ResourceIdentifier.fromString("registration/tenant");
    givenAStartedService().onSuccess(ok -> authService.verifyPlain(null, "hono-client@HONO", "secret", ctx.succeeding(res -> {
        ctx.verify(() -> {
            assertThat(res.getAuthorities().isAuthorized(registration, "assert")).isTrue();
            assertThat(res.getAuthorities().isAuthorized(registration, "add")).isTrue();
            ctx.completeNow();
        });
    })));
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) Test(org.junit.jupiter.api.Test)

Example 32 with ResourceIdentifier

use of org.eclipse.hono.util.ResourceIdentifier in project hono by eclipse.

the class AbstractProtocolAdapterBase method validateAddress.

/**
 * Validates a message's target address for consistency with Hono's addressing rules.
 *
 * @param address The address to validate.
 * @param authenticatedDevice The device that has uploaded the message.
 * @return A future indicating the outcome of the check.
 *         <p>
 *         The future will be completed with the validated target address if all
 *         checks succeed. Otherwise the future will be failed with a
 *         {@link ClientErrorException}.
 * @throws NullPointerException if address is {@code null}.
 */
protected final Future<ResourceIdentifier> validateAddress(final ResourceIdentifier address, final Device authenticatedDevice) {
    Objects.requireNonNull(address);
    final Promise<ResourceIdentifier> result = Promise.promise();
    if (authenticatedDevice == null) {
        if (Strings.isNullOrEmpty(address.getTenantId()) || Strings.isNullOrEmpty(address.getResourceId())) {
            result.fail(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "unauthenticated client must provide tenant and device ID in message address"));
        } else {
            result.complete(address);
        }
    } else {
        if (!Strings.isNullOrEmpty(address.getTenantId()) && Strings.isNullOrEmpty(address.getResourceId())) {
            result.fail(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "message address must not contain tenant ID only"));
        } else if (!Strings.isNullOrEmpty(address.getTenantId()) && !address.getTenantId().equals(authenticatedDevice.getTenantId())) {
            result.fail(new ClientErrorException(HttpURLConnection.HTTP_FORBIDDEN, "can only publish for device of same tenant"));
        } else if (Strings.isNullOrEmpty(address.getTenantId()) && Strings.isNullOrEmpty(address.getResourceId())) {
            // use authenticated device's tenant and device ID
            final ResourceIdentifier resource = ResourceIdentifier.from(address, authenticatedDevice.getTenantId(), authenticatedDevice.getDeviceId());
            result.complete(resource);
        } else if (Strings.isNullOrEmpty(address.getTenantId())) {
            // use authenticated device's tenant ID
            final ResourceIdentifier resource = ResourceIdentifier.from(address, authenticatedDevice.getTenantId(), address.getResourceId());
            result.complete(resource);
        } else {
            result.complete(address);
        }
    }
    return result.future().recover(t -> {
        log.debug("validation failed for address [{}], device [{}]: {}", address, authenticatedDevice, t.getMessage());
        return Future.failedFuture(t);
    });
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) ClientErrorException(org.eclipse.hono.client.ClientErrorException)

Example 33 with ResourceIdentifier

use of org.eclipse.hono.util.ResourceIdentifier in project hono by eclipse.

the class ProtonBasedMappingAndDelegatingCommandHandler method mapAndDelegateIncomingCommandMessage.

/**
 * Delegates an incoming command to the protocol adapter instance that the target
 * device is connected to.
 * <p>
 * Determines the target gateway (if applicable) and protocol adapter instance for an incoming command
 * and delegates the command to the resulting protocol adapter instance.
 *
 * @param tenantId The tenant that the command target must belong to.
 * @param messageDelivery The delivery of the command message.
 * @param message The command message.
 * @throws NullPointerException if any of the parameters is {@code null}.
 */
public void mapAndDelegateIncomingCommandMessage(final String tenantId, final ProtonDelivery messageDelivery, final Message message) {
    Objects.requireNonNull(tenantId);
    Objects.requireNonNull(messageDelivery);
    Objects.requireNonNull(message);
    final Timer.Sample timer = getMetrics().startTimer();
    // this is the place where a command message on the "command/${tenant}" address arrives *first*
    if (!ResourceIdentifier.isValid(message.getAddress())) {
        log.debug("command message has no valid address");
        final Rejected rejected = new Rejected();
        rejected.setError(new ErrorCondition(Constants.AMQP_BAD_REQUEST, "missing or invalid command target address"));
        messageDelivery.disposition(rejected, true);
        return;
    }
    final ResourceIdentifier targetAddress = ResourceIdentifier.fromString(message.getAddress());
    final String deviceId = targetAddress.getResourceId();
    if (!tenantId.equals(targetAddress.getTenantId())) {
        log.debug("command message address contains invalid tenant [expected: {}, found: {}]", tenantId, targetAddress.getTenantId());
        final Rejected rejected = new Rejected();
        rejected.setError(new ErrorCondition(AmqpError.UNAUTHORIZED_ACCESS, "unauthorized to send command to tenant"));
        messageDelivery.disposition(rejected, true);
        return;
    } else if (Strings.isNullOrEmpty(deviceId)) {
        log.debug("invalid command message address: {}", message.getAddress());
        final Rejected rejected = new Rejected();
        rejected.setError(new ErrorCondition(Constants.AMQP_BAD_REQUEST, "invalid command target address"));
        messageDelivery.disposition(rejected, true);
        return;
    }
    final ProtonBasedCommand command = ProtonBasedCommand.from(message);
    if (command.isValid()) {
        log.trace("received valid command message: {}", command);
    } else {
        log.debug("received invalid command message: {}", command);
    }
    final SpanContext spanContext = TracingHelper.extractSpanContext(tracer, message);
    final Span currentSpan = createSpan(tenantId, deviceId, spanContext);
    command.logToSpan(currentSpan);
    final ProtonBasedCommandContext commandContext = new ProtonBasedCommandContext(command, messageDelivery, currentSpan);
    if (command.isValid()) {
        mapAndDelegateIncomingCommand(commandContext, timer);
    } else {
        // command message is invalid
        commandContext.reject("malformed command message");
        reportInvalidCommand(commandContext, timer);
    }
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) SpanContext(io.opentracing.SpanContext) Timer(io.micrometer.core.instrument.Timer) ProtonBasedCommandContext(org.eclipse.hono.client.command.amqp.ProtonBasedCommandContext) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) ProtonBasedCommand(org.eclipse.hono.client.command.amqp.ProtonBasedCommand) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected) Span(io.opentracing.Span)

Example 34 with ResourceIdentifier

use of org.eclipse.hono.util.ResourceIdentifier in project hono by eclipse.

the class HttpBasedMessageMappingTest method testMapMessageSucceeds.

/**
 * Verifies that the result returned by the mapping service contains the
 * mapped payload, device ID and additional properties.
 *
 * @param ctx The helper to use for running tests on vert.x.
 */
@SuppressWarnings("unchecked")
@Test
public void testMapMessageSucceeds(final VertxTestContext ctx) {
    config.setMapperEndpoints(Map.of("mapper", MapperEndpoint.from("host", 1234, "/uri", false)));
    final ResourceIdentifier targetAddress = ResourceIdentifier.from(TelemetryConstants.TELEMETRY_ENDPOINT, TEST_TENANT_ID, "gateway");
    final String newDeviceId = "new-device";
    final HttpRequest<Buffer> httpRequest = mock(HttpRequest.class, withSettings().defaultAnswer(RETURNS_SELF));
    final MultiMap responseHeaders = MultiMap.caseInsensitiveMultiMap();
    responseHeaders.add(MessageHelper.APP_PROPERTY_DEVICE_ID, newDeviceId);
    responseHeaders.add("foo", "bar");
    final Buffer responseBody = Buffer.buffer("changed");
    final HttpResponse<Buffer> httpResponse = mock(HttpResponse.class);
    when(httpResponse.headers()).thenReturn(responseHeaders);
    when(httpResponse.bodyAsBuffer()).thenReturn(responseBody);
    when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK);
    when(mapperWebClient.post(anyInt(), anyString(), anyString())).thenReturn(httpRequest);
    final String topic = String.format("%s/?content-type=%s", TelemetryConstants.TELEMETRY_ENDPOINT, URLEncoder.encode("text/plain", StandardCharsets.UTF_8));
    final MqttPublishMessage message = newMessage(MqttQoS.AT_LEAST_ONCE, topic);
    final MqttContext context = newContext(message, span, new Device(TEST_TENANT_ID, "gateway"));
    final RegistrationAssertion assertion = new RegistrationAssertion("gateway").setDownstreamMessageMapper("mapper");
    messageMapping.mapDownstreamMessage(context, targetAddress, assertion).onComplete(ctx.succeeding(mappedMessage -> {
        ctx.verify(() -> {
            assertThat(mappedMessage.getTargetAddress().getResourceId()).isEqualTo("new-device");
            assertThat(mappedMessage.getPayload()).isEqualTo(responseBody);
            assertThat(mappedMessage.getAdditionalProperties()).doesNotContainKey(MessageHelper.APP_PROPERTY_DEVICE_ID);
            assertThat(mappedMessage.getAdditionalProperties()).containsEntry("foo", "bar");
        });
        ctx.completeNow();
    }));
    final ArgumentCaptor<Handler<AsyncResult<HttpResponse<Buffer>>>> handleCaptor = VertxMockSupport.argumentCaptorHandler();
    verify(httpRequest).sendBuffer(any(Buffer.class), handleCaptor.capture());
    handleCaptor.getValue().handle(Future.succeededFuture(httpResponse));
    final ArgumentCaptor<MultiMap> headersCaptor = ArgumentCaptor.forClass(MultiMap.class);
    verify(httpRequest).putHeaders(headersCaptor.capture());
    final MultiMap addedHeaders = headersCaptor.getValue();
    assertThat(addedHeaders.contains(MessageHelper.APP_PROPERTY_ORIG_ADDRESS, topic, false)).isTrue();
    assertThat(addedHeaders.contains(HttpHeaders.CONTENT_TYPE.toString(), "text/plain", false));
}
Also used : Buffer(io.vertx.core.buffer.Buffer) ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) HttpURLConnection(java.net.HttpURLConnection) VertxTestContext(io.vertx.junit5.VertxTestContext) BeforeEach(org.junit.jupiter.api.BeforeEach) MqttQoS(io.netty.handler.codec.mqtt.MqttQoS) HttpResponse(io.vertx.ext.web.client.HttpResponse) WebClient(io.vertx.ext.web.client.WebClient) Command(org.eclipse.hono.client.command.Command) MultiMap(io.vertx.core.MultiMap) MqttEndpoint(io.vertx.mqtt.MqttEndpoint) MqttPublishMessage(io.vertx.mqtt.messages.MqttPublishMessage) Constants(org.eclipse.hono.util.Constants) RETURNS_SELF(org.mockito.Mockito.RETURNS_SELF) TelemetryConstants(org.eclipse.hono.util.TelemetryConstants) ArgumentCaptor(org.mockito.ArgumentCaptor) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) Map(java.util.Map) MqttContext(org.eclipse.hono.adapter.mqtt.MqttContext) TracingMockSupport(org.eclipse.hono.test.TracingMockSupport) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) AsyncResult(io.vertx.core.AsyncResult) ArgumentMatchers.anyInt(org.mockito.ArgumentMatchers.anyInt) MapperEndpoint(org.eclipse.hono.config.MapperEndpoint) ServerErrorException(org.eclipse.hono.client.ServerErrorException) HttpHeaders(io.vertx.core.http.HttpHeaders) Mockito.times(org.mockito.Mockito.times) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) Mockito.when(org.mockito.Mockito.when) Truth.assertThat(com.google.common.truth.Truth.assertThat) MessageHelper(org.eclipse.hono.util.MessageHelper) VertxExtension(io.vertx.junit5.VertxExtension) Future(io.vertx.core.Future) StandardCharsets(java.nio.charset.StandardCharsets) Device(org.eclipse.hono.auth.Device) Mockito.verify(org.mockito.Mockito.verify) HttpRequest(io.vertx.ext.web.client.HttpRequest) Test(org.junit.jupiter.api.Test) Mockito.never(org.mockito.Mockito.never) URLEncoder(java.net.URLEncoder) Buffer(io.vertx.core.buffer.Buffer) VertxMockSupport(org.eclipse.hono.test.VertxMockSupport) MqttProtocolAdapterProperties(org.eclipse.hono.adapter.mqtt.MqttProtocolAdapterProperties) Span(io.opentracing.Span) Mockito.withSettings(org.mockito.Mockito.withSettings) Handler(io.vertx.core.Handler) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) Mockito.mock(org.mockito.Mockito.mock) MqttContext(org.eclipse.hono.adapter.mqtt.MqttContext) Device(org.eclipse.hono.auth.Device) Handler(io.vertx.core.Handler) HttpResponse(io.vertx.ext.web.client.HttpResponse) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) MultiMap(io.vertx.core.MultiMap) MqttPublishMessage(io.vertx.mqtt.messages.MqttPublishMessage) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) Test(org.junit.jupiter.api.Test)

Example 35 with ResourceIdentifier

use of org.eclipse.hono.util.ResourceIdentifier in project hono by eclipse.

the class HttpBasedMessageMappingTest method testMappingFailsForWhenPayloadCannotMapped.

/**
 * Verifies that the downstream mapper returns a failed future with a ServerErrorException if the downstream mapper has been configured
 * for an adapter but the remote service returns a 403 status code indicating that the device payload cannot be mapped.
 *
 * @param ctx   The Vert.x test context.
 */
@Test
@SuppressWarnings("unchecked")
public void testMappingFailsForWhenPayloadCannotMapped(final VertxTestContext ctx) {
    config.setMapperEndpoints(Map.of("mapper", MapperEndpoint.from("host", 1234, "/uri", false)));
    final ResourceIdentifier targetAddress = ResourceIdentifier.from(TelemetryConstants.TELEMETRY_ENDPOINT, TEST_TENANT_ID, "gateway");
    final HttpRequest<Buffer> httpRequest = mock(HttpRequest.class, withSettings().defaultAnswer(RETURNS_SELF));
    final HttpResponse<Buffer> httpResponse = mock(HttpResponse.class);
    when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_FORBIDDEN);
    when(mapperWebClient.post(anyInt(), anyString(), anyString())).thenReturn(httpRequest);
    final MqttPublishMessage message = newMessage(MqttQoS.AT_LEAST_ONCE, "mqtt-topic");
    final MqttContext context = newContext(message, span, new Device(TEST_TENANT_ID, "gateway"));
    final RegistrationAssertion assertion = new RegistrationAssertion("gateway").setDownstreamMessageMapper("mapper");
    messageMapping.mapDownstreamMessage(context, targetAddress, assertion).onComplete(ctx.failing(t -> {
        ctx.verify(() -> {
            assertThat(t).isInstanceOf(ServerErrorException.class);
            assertThat((((ServerErrorException) t).getErrorCode())).isEqualTo(HttpURLConnection.HTTP_UNAVAILABLE);
        });
        ctx.completeNow();
    }));
    final ArgumentCaptor<Handler<AsyncResult<HttpResponse<Buffer>>>> handlerCaptor = VertxMockSupport.argumentCaptorHandler();
    verify(httpRequest).sendBuffer(any(Buffer.class), handlerCaptor.capture());
    handlerCaptor.getValue().handle(Future.succeededFuture(httpResponse));
}
Also used : Buffer(io.vertx.core.buffer.Buffer) ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) HttpURLConnection(java.net.HttpURLConnection) VertxTestContext(io.vertx.junit5.VertxTestContext) BeforeEach(org.junit.jupiter.api.BeforeEach) MqttQoS(io.netty.handler.codec.mqtt.MqttQoS) HttpResponse(io.vertx.ext.web.client.HttpResponse) WebClient(io.vertx.ext.web.client.WebClient) Command(org.eclipse.hono.client.command.Command) MultiMap(io.vertx.core.MultiMap) MqttEndpoint(io.vertx.mqtt.MqttEndpoint) MqttPublishMessage(io.vertx.mqtt.messages.MqttPublishMessage) Constants(org.eclipse.hono.util.Constants) RETURNS_SELF(org.mockito.Mockito.RETURNS_SELF) TelemetryConstants(org.eclipse.hono.util.TelemetryConstants) ArgumentCaptor(org.mockito.ArgumentCaptor) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) Map(java.util.Map) MqttContext(org.eclipse.hono.adapter.mqtt.MqttContext) TracingMockSupport(org.eclipse.hono.test.TracingMockSupport) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) AsyncResult(io.vertx.core.AsyncResult) ArgumentMatchers.anyInt(org.mockito.ArgumentMatchers.anyInt) MapperEndpoint(org.eclipse.hono.config.MapperEndpoint) ServerErrorException(org.eclipse.hono.client.ServerErrorException) HttpHeaders(io.vertx.core.http.HttpHeaders) Mockito.times(org.mockito.Mockito.times) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) Mockito.when(org.mockito.Mockito.when) Truth.assertThat(com.google.common.truth.Truth.assertThat) MessageHelper(org.eclipse.hono.util.MessageHelper) VertxExtension(io.vertx.junit5.VertxExtension) Future(io.vertx.core.Future) StandardCharsets(java.nio.charset.StandardCharsets) Device(org.eclipse.hono.auth.Device) Mockito.verify(org.mockito.Mockito.verify) HttpRequest(io.vertx.ext.web.client.HttpRequest) Test(org.junit.jupiter.api.Test) Mockito.never(org.mockito.Mockito.never) URLEncoder(java.net.URLEncoder) Buffer(io.vertx.core.buffer.Buffer) VertxMockSupport(org.eclipse.hono.test.VertxMockSupport) MqttProtocolAdapterProperties(org.eclipse.hono.adapter.mqtt.MqttProtocolAdapterProperties) Span(io.opentracing.Span) Mockito.withSettings(org.mockito.Mockito.withSettings) Handler(io.vertx.core.Handler) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) Mockito.mock(org.mockito.Mockito.mock) MqttContext(org.eclipse.hono.adapter.mqtt.MqttContext) Device(org.eclipse.hono.auth.Device) Handler(io.vertx.core.Handler) HttpResponse(io.vertx.ext.web.client.HttpResponse) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) MqttPublishMessage(io.vertx.mqtt.messages.MqttPublishMessage) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) ServerErrorException(org.eclipse.hono.client.ServerErrorException) Test(org.junit.jupiter.api.Test)

Aggregations

ResourceIdentifier (org.eclipse.hono.util.ResourceIdentifier)82 Message (org.apache.qpid.proton.message.Message)30 Future (io.vertx.core.Future)24 HttpURLConnection (java.net.HttpURLConnection)22 MessageHelper (org.eclipse.hono.util.MessageHelper)22 ClientErrorException (org.eclipse.hono.client.ClientErrorException)20 Test (org.junit.Test)20 Test (org.junit.jupiter.api.Test)19 Handler (io.vertx.core.Handler)18 Map (java.util.Map)18 Span (io.opentracing.Span)17 Buffer (io.vertx.core.buffer.Buffer)17 SpanContext (io.opentracing.SpanContext)16 Constants (org.eclipse.hono.util.Constants)16 Promise (io.vertx.core.Promise)15 Objects (java.util.Objects)14 AsyncResult (io.vertx.core.AsyncResult)13 Vertx (io.vertx.core.Vertx)13 ProtonConnection (io.vertx.proton.ProtonConnection)13 ProtonReceiver (io.vertx.proton.ProtonReceiver)13