Search in sources :

Example 6 with ResourceIdentifier

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

the class MessageForwardingEndpointTest method testMessageHandlerRejectsMalformedMessage.

/**
 * Verifies that the endpoint rejects messages that do not pass formal verification.
 */
@SuppressWarnings({ "unchecked" })
@Test
public void testMessageHandlerRejectsMalformedMessage() {
    // GIVEN an endpoint with an attached client
    final ResourceIdentifier targetAddress = ResourceIdentifier.fromString("telemetry/tenant");
    final ProtonConnection connection = mock(ProtonConnection.class);
    when(connection.getRemoteContainer()).thenReturn("test-client");
    final ProtonReceiver receiver = mock(ProtonReceiver.class);
    when(receiver.getRemoteQoS()).thenReturn(ProtonQoS.AT_MOST_ONCE);
    final DownstreamAdapter adapter = mock(DownstreamAdapter.class);
    doAnswer(invocation -> {
        final Handler<AsyncResult<Void>> resultHandler = invocation.getArgument(1);
        resultHandler.handle(Future.succeededFuture());
        return null;
    }).when(adapter).onClientAttach(any(UpstreamReceiver.class), any(Handler.class));
    final MessageForwardingEndpoint<HonoMessagingConfigProperties> endpoint = getEndpoint(false);
    endpoint.setDownstreamAdapter(adapter);
    endpoint.onLinkAttach(connection, receiver, targetAddress);
    final ArgumentCaptor<ProtonMessageHandler> messageHandler = ArgumentCaptor.forClass(ProtonMessageHandler.class);
    verify(receiver).handler(messageHandler.capture());
    // WHEN a client sends a malformed message
    final Message message = ProtonHelper.message("malformed");
    final ProtonDelivery upstreamDelivery = mock(ProtonDelivery.class);
    messageHandler.getValue().handle(upstreamDelivery, message);
    // THEN the endpoint rejects the message
    final ArgumentCaptor<Rejected> deliveryState = ArgumentCaptor.forClass(Rejected.class);
    verify(upstreamDelivery).disposition(deliveryState.capture(), eq(Boolean.TRUE));
    assertThat(deliveryState.getValue().getError().getCondition(), is(AmqpError.DECODE_ERROR));
    // but does not close the link
    verify(receiver, never()).close();
    // and the message is not forwarded to the downstream adapter
    verify(adapter, never()).processMessage(any(UpstreamReceiver.class), eq(upstreamDelivery), eq(message));
}
Also used : ProtonReceiver(io.vertx.proton.ProtonReceiver) Message(org.apache.qpid.proton.message.Message) ProtonDelivery(io.vertx.proton.ProtonDelivery) ProtonMessageHandler(io.vertx.proton.ProtonMessageHandler) Handler(io.vertx.core.Handler) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) ProtonConnection(io.vertx.proton.ProtonConnection) ProtonMessageHandler(io.vertx.proton.ProtonMessageHandler) AsyncResult(io.vertx.core.AsyncResult) Test(org.junit.Test)

Example 7 with ResourceIdentifier

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

the class TenantMessageFilterTest method testVerifyDetectsMissingTenantProperty.

/**
 * Verifies that {@link TenantMessageFilter#verify(ResourceIdentifier, Message)} detects if the mandatory tenantId
 * property is missing.
 */
@Test
public void testVerifyDetectsMissingTenantProperty() {
    // GIVEN a valid tenant GET message without an AMQP value
    final Message msg = givenAMessageHavingProperties(TenantConstants.TenantAction.get);
    // WHEN receiving the message via a link with any tenant
    final ResourceIdentifier linkTarget = getResourceIdentifier(DEFAULT_TENANT);
    // THEN message validation fails
    assertFalse(TenantMessageFilter.verify(linkTarget, msg));
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) Message(org.apache.qpid.proton.message.Message) Test(org.junit.Test)

Example 8 with ResourceIdentifier

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

the class RequestResponseEndpoint method onLinkAttach.

/**
 * Handles a client's request to establish a link for receiving responses
 * to service invocations.
 * <p>
 * This method registers a consumer on the vert.x event bus for the given reply-to address.
 * Response messages received over the event bus are transformed into AMQP messages using
 * the {@link #getAmqpReply(EventBusMessage)} method and sent to the client over the established
 * link.
 *
 * @param con The AMQP connection that the link is part of.
 * @param sender The link to establish.
 * @param replyToAddress The reply-to address to create a consumer on the event bus for.
 */
@Override
public final void onLinkAttach(final ProtonConnection con, final ProtonSender sender, final ResourceIdentifier replyToAddress) {
    if (isValidReplyToAddress(replyToAddress)) {
        logger.debug("establishing sender link with client [{}]", sender.getName());
        final MessageConsumer<JsonObject> replyConsumer = vertx.eventBus().consumer(replyToAddress.toString(), message -> {
            // TODO check for correct session here...?
            if (logger.isTraceEnabled()) {
                logger.trace("forwarding reply to client [{}]: {}", sender.getName(), message.body().encodePrettily());
            }
            final EventBusMessage response = EventBusMessage.fromJson(message.body());
            filterResponse(Constants.getClientPrincipal(con), response).recover(t -> {
                final int status = Optional.of(t).map(cause -> {
                    if (cause instanceof ServiceInvocationException) {
                        return ((ServiceInvocationException) cause).getErrorCode();
                    } else {
                        return null;
                    }
                }).orElse(HttpURLConnection.HTTP_INTERNAL_ERROR);
                return Future.succeededFuture(response.getResponse(status));
            }).map(filteredResponse -> {
                final Message amqpReply = getAmqpReply(filteredResponse);
                sender.send(amqpReply);
                return null;
            });
        });
        sender.setQoS(ProtonQoS.AT_LEAST_ONCE);
        sender.closeHandler(senderClosed -> {
            logger.debug("client [{}] closed sender link, removing associated event bus consumer [{}]", sender.getName(), replyConsumer.address());
            replyConsumer.unregister();
            if (senderClosed.succeeded()) {
                senderClosed.result().close();
            }
        });
        sender.open();
    } else {
        logger.debug("client [{}] provided invalid reply-to address", sender.getName());
        sender.setCondition(ProtonHelper.condition(AmqpError.INVALID_FIELD, String.format("reply-to address must have the following format %s/<tenant>/<reply-address>", getName())));
        sender.close();
    }
}
Also used : HttpURLConnection(java.net.HttpURLConnection) ProtonConnection(io.vertx.proton.ProtonConnection) ProtonReceiver(io.vertx.proton.ProtonReceiver) AmqpErrorException(org.eclipse.hono.util.AmqpErrorException) ProtonDelivery(io.vertx.proton.ProtonDelivery) DecodeException(io.vertx.core.json.DecodeException) Autowired(org.springframework.beans.factory.annotation.Autowired) EventBusMessage(org.eclipse.hono.util.EventBusMessage) HonoUser(org.eclipse.hono.auth.HonoUser) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) ServiceConfigProperties(org.eclipse.hono.config.ServiceConfigProperties) Constants(org.eclipse.hono.util.Constants) Message(org.apache.qpid.proton.message.Message) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) JsonObject(io.vertx.core.json.JsonObject) AmqpError(org.apache.qpid.proton.amqp.transport.AmqpError) Vertx(io.vertx.core.Vertx) ProtonHelper(io.vertx.proton.ProtonHelper) ProtonQoS(io.vertx.proton.ProtonQoS) MessageHelper(org.eclipse.hono.util.MessageHelper) Future(io.vertx.core.Future) Objects(java.util.Objects) Optional(java.util.Optional) ProtonSender(io.vertx.proton.ProtonSender) ClaimsBasedAuthorizationService(org.eclipse.hono.service.auth.ClaimsBasedAuthorizationService) MessageConsumer(io.vertx.core.eventbus.MessageConsumer) AuthorizationService(org.eclipse.hono.service.auth.AuthorizationService) EventBusMessage(org.eclipse.hono.util.EventBusMessage) Message(org.apache.qpid.proton.message.Message) EventBusMessage(org.eclipse.hono.util.EventBusMessage) JsonObject(io.vertx.core.json.JsonObject) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException)

Example 9 with ResourceIdentifier

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

the class TenantAmqpEndpoint method isAuthorized.

/**
 * Checks if the client of the tenant API is authorized to execute a given operation.
 * <p>
 * This check makes use of the provided tenantId by enriching the resource with it.
 * So permission checks can be done on a per tenant level, although the endpoint the client
 * connects to does not include the tenantId (like for several other of Hono's APIs).
 *
 * @param clientPrincipal The client.
 * @param resource The resource the operation belongs to.
 * @param message The message for which the authorization shall be checked.
 * @return The outcome of the check.
 * @throws NullPointerException if any of the parameters is {@code null}.
 */
@Override
protected Future<Boolean> isAuthorized(final HonoUser clientPrincipal, final ResourceIdentifier resource, final Message message) {
    Objects.requireNonNull(message);
    final String tenantId = MessageHelper.getTenantId(message);
    final ResourceIdentifier specificTenantAddress = ResourceIdentifier.from(resource.getEndpoint(), tenantId, null);
    return getAuthorizationService().isAuthorized(clientPrincipal, specificTenantAddress, message.getSubject());
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier)

Example 10 with ResourceIdentifier

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

the class KuraProtocolAdapter method mapTopic.

Future<ResourceIdentifier> mapTopic(final MqttContext ctx) {
    final Future<ResourceIdentifier> result = Future.future();
    try {
        final ResourceIdentifier topic = ResourceIdentifier.fromString(ctx.message().topicName());
        ResourceIdentifier mappedTopic = null;
        if (getConfig().getControlPrefix().equals(topic.getEndpoint())) {
            // this is a "control" message
            ctx.setContentType(getConfig().getCtrlMsgContentType());
            final String[] mappedPath = Arrays.copyOf(topic.getResourcePath(), topic.getResourcePath().length);
            mappedPath[0] = getEndpoint(ctx.message().qosLevel());
            mappedTopic = ResourceIdentifier.fromPath(mappedPath);
        } else {
            // map "data" messages based on QoS
            ctx.setContentType(getConfig().getDataMsgContentType());
            final String[] mappedPath = new String[topic.getResourcePath().length + 1];
            System.arraycopy(topic.getResourcePath(), 0, mappedPath, 1, topic.getResourcePath().length);
            mappedPath[0] = getEndpoint(ctx.message().qosLevel());
            mappedTopic = ResourceIdentifier.fromPath(mappedPath);
        }
        if (mappedTopic.getResourcePath().length < 3) {
            // topic does not contain account_name and client_id
            result.fail(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "topic does not comply with Kura format"));
        } else {
            LOG.debug("mapped Kura message [topic: {}, QoS: {}] to Hono message [to: {}, device_id: {}, content-type: {}]", topic, ctx.message().qosLevel(), mappedTopic.getBasePath(), mappedTopic.getResourceId(), ctx.contentType());
            result.complete(mappedTopic);
        }
    } catch (final IllegalArgumentException e) {
        result.fail(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "malformed topic name"));
    }
    return result;
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) ClientErrorException(org.eclipse.hono.client.ClientErrorException)

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