use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapterTest method testUploadEventRejectsPresettledMessage.
/**
* Verifies that the adapter rejects presettled messages with an event address.
*
* @param ctx The vert.x test context.
*/
@Test
public void testUploadEventRejectsPresettledMessage(final VertxTestContext ctx) {
// GIVEN an adapter
givenAnAdapter(properties);
givenAnEventSenderForAnyTenant();
// with an enabled tenant
givenAConfiguredTenant(TEST_TENANT_ID, true);
// WHEN a device uploads an event using a presettled message
final Device gateway = new Device(TEST_TENANT_ID, "device");
final ProtonDelivery delivery = mock(ProtonDelivery.class);
// AT MOST ONCE
when(delivery.remotelySettled()).thenReturn(true);
final String to = ResourceIdentifier.fromString(EventConstants.EVENT_ENDPOINT).toString();
final Buffer payload = Buffer.buffer("some payload");
adapter.onMessageReceived(AmqpContext.fromMessage(delivery, getFakeMessage(to, payload), span, gateway)).onComplete(ctx.failing(t -> {
ctx.verify(() -> {
// THEN the adapter does not forward the event
assertNoEventHasBeenSentDownstream();
// AND notifies the device by sending back a REJECTED disposition
verify(delivery).disposition(any(Rejected.class), eq(true));
});
ctx.completeNow();
}));
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapterTest method testUploadEventFailsForGatewayOfDifferentTenant.
/**
* Verifies that a request from a gateway to upload an event on behalf of a device that belongs
* to another tenant than the gateway fails.
*
* @param ctx The vert.x test context.
*/
@Test
public void testUploadEventFailsForGatewayOfDifferentTenant(final VertxTestContext ctx) {
// GIVEN an adapter
givenAnAdapter(properties);
givenAnEventSenderForAnyTenant();
// with an enabled tenant
givenAConfiguredTenant(TEST_TENANT_ID, true);
// WHEN a gateway uploads an event on behalf of a device of another tenant
final Device gateway = new Device(TEST_TENANT_ID, "gw");
final ProtonDelivery delivery = mock(ProtonDelivery.class);
// AT LEAST ONCE
when(delivery.remotelySettled()).thenReturn(false);
final String to = ResourceIdentifier.from(EventConstants.EVENT_ENDPOINT, "other-tenant", TEST_DEVICE).toString();
final Buffer payload = Buffer.buffer("some payload");
adapter.onMessageReceived(AmqpContext.fromMessage(delivery, getFakeMessage(to, payload), span, gateway)).onComplete(ctx.failing(t -> {
ctx.verify(() -> {
// THEN the adapter does not send the event
assertNoEventHasBeenSentDownstream();
// AND notifies the device by sending back a REJECTED disposition
verify(delivery).disposition(any(Rejected.class), eq(true));
});
ctx.completeNow();
}));
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapterTest method testUploadCommandResponseWithoutPayloadSucceeds.
/**
* Verify that the AMQP adapter forwards command responses that do not contain a payload downstream.
*
* @param ctx The vert.x test context.
*/
@Test
public void testUploadCommandResponseWithoutPayloadSucceeds(final VertxTestContext ctx) {
// GIVEN an AMQP adapter
givenAnAdapter(properties);
final CommandResponseSender responseSender = givenACommandResponseSenderForAnyTenant();
when(responseSender.sendCommandResponse(any(TenantObject.class), any(RegistrationAssertion.class), any(CommandResponse.class), (SpanContext) any())).thenReturn(Future.succeededFuture());
// which is enabled for the test tenant
final TenantObject tenantObject = givenAConfiguredTenant(TEST_TENANT_ID, true);
// WHEN an unauthenticated device publishes a command response
final String replyToAddress = String.format("%s/%s/%s", getCommandResponseEndpoint(), TEST_TENANT_ID, Commands.getDeviceFacingReplyToId("test-reply-id", TEST_DEVICE, MessagingType.amqp));
final Map<String, Object> propertyMap = new HashMap<>();
propertyMap.put(MessageHelper.APP_PROPERTY_STATUS, 200);
final ApplicationProperties props = new ApplicationProperties(propertyMap);
final Message message = getFakeMessage(replyToAddress, null);
message.setCorrelationId("correlation-id");
message.setApplicationProperties(props);
final ProtonDelivery delivery = mock(ProtonDelivery.class);
adapter.onMessageReceived(AmqpContext.fromMessage(delivery, message, span, null)).onComplete(ctx.succeeding(ok -> {
ctx.verify(() -> {
// THEN the adapter forwards the command response message downstream
verify(responseSender).sendCommandResponse(eq(tenantObject), any(RegistrationAssertion.class), any(CommandResponse.class), (SpanContext) any());
// and reports the forwarded message
verify(metrics).reportCommand(eq(Direction.RESPONSE), eq(TEST_TENANT_ID), eq(tenantObject), eq(ProcessingOutcome.FORWARDED), eq(0), any());
});
ctx.completeNow();
}));
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapter method openCommandSenderLink.
private Future<CommandConsumer> openCommandSenderLink(final ProtonConnection connection, final ProtonSender sender, final ResourceIdentifier address, final Device authenticatedDevice, final Span span, final OptionalInt traceSamplingPriority) {
return createCommandConsumer(sender, address, authenticatedDevice, span).map(consumer -> {
final String tenantId = address.getTenantId();
final String deviceId = address.getResourceId();
sender.setSource(sender.getRemoteSource());
sender.setTarget(sender.getRemoteTarget());
sender.setQoS(ProtonQoS.AT_LEAST_ONCE);
final Handler<AsyncResult<ProtonSender>> detachHandler = link -> {
final Span detachHandlerSpan = newSpan("detach device command receiver link", authenticatedDevice, traceSamplingPriority);
removeCommandSubscription(connection, address.toString());
onLinkDetach(sender);
closeCommandConsumer(consumer, address, authenticatedDevice, true, detachHandlerSpan).onComplete(v -> detachHandlerSpan.finish());
};
HonoProtonHelper.setCloseHandler(sender, detachHandler);
HonoProtonHelper.setDetachHandler(sender, detachHandler);
sender.open();
// At this point, the remote peer's receiver link is successfully opened and is ready to receive
// commands. Send "device ready for command" notification downstream.
log.debug("established link [address: {}] for sending commands to device", address);
sendConnectedTtdEvent(tenantId, deviceId, authenticatedDevice, span.context());
registerCommandSubscription(connection, new CommandSubscription(consumer, address));
return consumer;
}).recover(t -> Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "cannot create command consumer")));
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapter method handleRemoteReceiverOpen.
/**
* This method is invoked when a device wants to open a link for uploading messages.
* <p>
* The same link is used by the device to upload telemetry data, events and command
* responses to be forwarded downstream.
* <p>
* If the attach frame contains a target address, this method simply closes the link,
* otherwise, it accepts and opens the link.
*
* @param conn The connection through which the request is initiated.
* @param receiver The receiver link for receiving the data.
*/
protected void handleRemoteReceiverOpen(final ProtonConnection conn, final ProtonReceiver receiver) {
final Device authenticatedDevice = getAuthenticatedDevice(conn);
final OptionalInt traceSamplingPriority = getTraceSamplingPriority(conn);
final Span span = newSpan("attach device sender link", authenticatedDevice, traceSamplingPriority);
span.log(Map.of("snd-settle-mode", receiver.getRemoteQoS()));
final String remoteTargetAddress = Optional.ofNullable(receiver.getRemoteTarget()).map(Target::getAddress).orElse(null);
if (!Strings.isNullOrEmpty(remoteTargetAddress)) {
log.debug("client provided target address [{}] in open frame, closing link [container: {}, {}]", remoteTargetAddress, conn.getRemoteContainer(), authenticatedDevice);
span.log(Map.of("target address", remoteTargetAddress));
final Exception ex = new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "container supports anonymous terminus only");
closeLinkWithError(receiver, ex, span);
} else {
receiver.setTarget(receiver.getRemoteTarget());
receiver.setSource(receiver.getRemoteSource());
receiver.setQoS(receiver.getRemoteQoS());
receiver.setPrefetch(30);
// manage disposition handling manually
receiver.setAutoAccept(false);
receiver.maxMessageSizeExceededHandler(recv -> {
final Span errorSpan = newSpan("upload message", authenticatedDevice, traceSamplingPriority);
log.debug("incoming message size exceeds configured maximum of {} bytes; link will be detached [container: {}, {}]", getConfig().getMaxPayloadSize(), conn.getRemoteContainer(), authenticatedDevice);
TracingHelper.logError(errorSpan, String.format("incoming message size exceeds configured maximum of %s bytes", getConfig().getMaxPayloadSize()));
errorSpan.log("device sender link will be detached");
errorSpan.finish();
});
HonoProtonHelper.setCloseHandler(receiver, remoteDetach -> onLinkDetach(receiver));
HonoProtonHelper.setDetachHandler(receiver, remoteDetach -> onLinkDetach(receiver));
receiver.handler((delivery, message) -> {
try {
final SpanContext spanContext = TracingHelper.extractSpanContext(tracer, message);
final Span msgSpan = newSpan("upload message", authenticatedDevice, traceSamplingPriority, spanContext);
HonoProtonHelper.onReceivedMessageDeliveryUpdatedFromRemote(delivery, d -> {
log.debug("got unexpected disposition update for message received from device [remote state: {}, container: {}, {}]", delivery.getRemoteState(), conn.getRemoteContainer(), authenticatedDevice);
msgSpan.log("got unexpected disposition from device [remote state: " + delivery.getRemoteState() + "]");
});
msgSpan.log(Map.of(Tags.MESSAGE_BUS_DESTINATION.getKey(), message.getAddress(), "settled", delivery.remotelySettled()));
final AmqpContext ctx = AmqpContext.fromMessage(delivery, message, msgSpan, authenticatedDevice);
ctx.setTimer(metrics.startTimer());
final Future<Void> spanPreparationFuture = authenticatedDevice == null ? applyTraceSamplingPriorityForAddressTenant(ctx.getAddress(), msgSpan) : Future.succeededFuture();
spanPreparationFuture.compose(ar -> onMessageReceived(ctx).onSuccess(ok -> msgSpan.finish()).onFailure(error -> closeConnectionOnTerminalError(error, conn, ctx, msgSpan)));
} catch (final Exception ex) {
log.warn("error handling message [container: {}, {}]", conn.getRemoteContainer(), authenticatedDevice, ex);
if (!conn.isDisconnected()) {
ProtonHelper.released(delivery, true);
}
}
});
receiver.open();
log.debug("established link for receiving messages from device [container: {}, {}]", conn.getRemoteContainer(), authenticatedDevice);
span.log("link established");
}
span.finish();
}
Aggregations