use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapter method onMessageReceived.
/**
* Processes an AMQP message received from a device.
* <p>
* This method settles the transfer with the following outcomes:
* <ul>
* <li><em>accepted</em> if the message has been successfully processed.</li>
* <li><em>rejected</em> if the message could not be processed due to a problem caused by the device.</li>
* <li><em>released</em> if the message could not be forwarded to a downstream consumer.</li>
* </ul>
*
* @param ctx The context for the message.
* @return A future indicating the outcome of processing the message.
* The future will succeed if the message has been processed successfully, otherwise it
* will fail with a {@link ServiceInvocationException}.
*/
protected Future<Void> onMessageReceived(final AmqpContext ctx) {
log.trace("processing message [address: {}, qos: {}]", ctx.getAddress(), ctx.getRequestedQos());
final Span msgSpan = ctx.getTracingSpan();
return validateEndpoint(ctx).compose(validatedEndpoint -> validateAddress(validatedEndpoint.getAddress(), validatedEndpoint.getAuthenticatedDevice())).compose(validatedAddress -> uploadMessage(ctx, validatedAddress, msgSpan)).map(d -> {
ProtonHelper.accepted(ctx.delivery(), true);
return d;
}).recover(t -> {
if (t instanceof ClientErrorException) {
MessageHelper.rejected(ctx.delivery(), getErrorCondition(t));
} else {
ProtonHelper.released(ctx.delivery(), true);
}
log.debug("failed to process message from device", t);
TracingHelper.logError(msgSpan, t);
return Future.failedFuture(t);
});
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapter method onConnectRequest.
/**
* Handles a remote peer's request to open a connection.
*
* @param con The connection to be opened.
*/
protected void onConnectRequest(final ProtonConnection con) {
con.disconnectHandler(lostConnection -> {
log.debug("lost connection to device [container: {}]", con.getRemoteContainer());
onConnectionLoss(con);
});
con.closeHandler(remoteClose -> {
handleRemoteConnectionClose(con, remoteClose);
onConnectionLoss(con);
});
// when a begin frame is received
con.sessionOpenHandler(session -> {
HonoProtonHelper.setDefaultCloseHandler(session);
handleSessionOpen(con, session);
});
// when the device wants to open a link for
// uploading messages
con.receiverOpenHandler(receiver -> {
HonoProtonHelper.setDefaultCloseHandler(receiver);
receiver.setMaxMessageSize(UnsignedLong.valueOf(getConfig().getMaxPayloadSize()));
handleRemoteReceiverOpen(con, receiver);
});
// when the device wants to open a link for
// receiving commands
con.senderOpenHandler(sender -> {
handleRemoteSenderOpenForCommands(con, sender);
});
con.openHandler(remoteOpen -> {
final Device authenticatedDevice = getAuthenticatedDevice(con);
if (authenticatedDevice == null) {
metrics.incrementUnauthenticatedConnections();
} else {
metrics.incrementConnections(authenticatedDevice.getTenantId());
}
if (remoteOpen.failed()) {
log.debug("ignoring device's open frame containing error", remoteOpen.cause());
} else {
processRemoteOpen(remoteOpen.result());
}
});
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapter method handleRemoteSenderOpenForCommands.
/**
* This method is invoked when a device wants to open a link for receiving commands.
* <p>
* The link will be closed immediately if
* <ul>
* <li>the device does not specify a source address for its receive link or</li>
* <li>the source address cannot be parsed or does not point to the command endpoint or</li>
* <li>the AMQP adapter is disabled for the tenant that the device belongs to.</li>
* </ul>
*
* @param connection The AMQP connection to the device.
* @param sender The link to use for sending commands to the device.
*/
protected void handleRemoteSenderOpenForCommands(final ProtonConnection connection, final ProtonSender sender) {
final Device authenticatedDevice = getAuthenticatedDevice(connection);
final OptionalInt traceSamplingPriority = getTraceSamplingPriority(connection);
final Span span = newSpan("attach device command receiver link", authenticatedDevice, traceSamplingPriority);
getResourceIdentifier(sender.getRemoteSource()).compose(address -> validateAddress(address, authenticatedDevice)).compose(validAddress -> {
// validAddress ALWAYS contains the tenant and device ID
if (CommandConstants.isCommandEndpoint(validAddress.getEndpoint())) {
return openCommandSenderLink(connection, sender, validAddress, authenticatedDevice, span, traceSamplingPriority);
} else {
return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_NOT_FOUND, "no such node"));
}
}).map(consumer -> {
span.log("link established");
return consumer;
}).recover(t -> {
if (t instanceof ServiceInvocationException) {
closeLinkWithError(sender, t, span);
} else {
closeLinkWithError(sender, new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "Invalid source address"), span);
}
return Future.failedFuture(t);
}).onComplete(s -> {
span.finish();
});
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapter method checkAuthorizationAndResourceLimits.
private Future<Void> checkAuthorizationAndResourceLimits(final Device authenticatedDevice, final ProtonConnection con, final Span span) {
final Promise<Void> connectAuthorizationCheck = Promise.promise();
if (getConfig().isAuthenticationRequired()) {
if (authenticatedDevice == null) {
connectAuthorizationCheck.fail(new ClientErrorException(HttpURLConnection.HTTP_UNAUTHORIZED, "anonymous devices not supported"));
} else {
log.trace("received connection request from {}", authenticatedDevice);
// the SASL handshake will already have authenticated the device
// we still need to verify that
// the adapter is enabled for the tenant,
// the device/gateway exists and is enabled and
// that the connection limit for the tenant is not exceeded.
CompositeFuture.all(checkDeviceRegistration(authenticatedDevice, span.context()), getTenantConfiguration(authenticatedDevice.getTenantId(), span.context()).compose(tenantConfig -> CompositeFuture.all(isAdapterEnabled(tenantConfig), checkConnectionLimit(tenantConfig, span.context())))).map(ok -> {
log.debug("{} is registered and enabled", authenticatedDevice);
span.log(String.format("device [%s] is registered and enabled", authenticatedDevice));
return (Void) null;
}).onComplete(connectAuthorizationCheck);
}
} else {
log.trace("received connection request from anonymous device [container: {}]", con.getRemoteContainer());
connectAuthorizationCheck.complete();
}
return connectAuthorizationCheck.future();
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class CommandResponseResourceTest method testUploadCommandResponseFailsForDisabledTenant.
/**
* Verifies that the adapter fails the upload of a command response with a 4.03
* if the adapter is disabled for the device's tenant.
*
* @param ctx The vert.x test context.
*/
@Test
public void testUploadCommandResponseFailsForDisabledTenant(final VertxTestContext ctx) {
// GIVEN an adapter
givenAnAdapter(properties);
final var resource = givenAResource(adapter);
final Promise<Void> outcome = Promise.promise();
final CommandResponseSender sender = givenACommandResponseSenderForAnyTenant(outcome);
// that is not enabled for a device's tenant
when(adapter.isAdapterEnabled(any(TenantObject.class))).thenReturn(Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_FORBIDDEN)));
// WHEN a device publishes an command response
final String reqId = Commands.encodeRequestIdParameters("correlation", "replyToId", "device", MessagingType.amqp);
final Buffer payload = Buffer.buffer("some payload");
final OptionSet options = new OptionSet();
options.addUriPath(CommandConstants.COMMAND_RESPONSE_ENDPOINT).addUriPath(reqId);
options.addUriQuery(String.format("%s=%d", Constants.HEADER_COMMAND_RESPONSE_STATUS, 200));
options.setContentFormat(MediaTypeRegistry.TEXT_PLAIN);
final CoapExchange coapExchange = newCoapExchange(payload, Type.CON, options);
final Device authenticatedDevice = new Device("tenant", "device");
final CoapContext context = CoapContext.fromRequest(coapExchange, authenticatedDevice, authenticatedDevice, "device", span);
resource.uploadCommandResponseMessage(context).onComplete(ctx.failing(t -> {
ctx.verify(() -> {
// THEN the command response has not been forwarded downstream
verify(sender, never()).sendCommandResponse(any(TenantObject.class), any(RegistrationAssertion.class), any(CommandResponse.class), any(SpanContext.class));
// and the device gets a 4.03 response
assertThat(t).isInstanceOf(ClientErrorException.class);
assertThat(((ClientErrorException) t).getErrorCode()).isEqualTo(HttpURLConnection.HTTP_FORBIDDEN);
// and the response has not been reported as forwarded
verify(metrics, never()).reportCommand(eq(Direction.RESPONSE), eq("tenant"), any(), eq(ProcessingOutcome.FORWARDED), anyInt(), any());
});
ctx.completeNow();
}));
}
Aggregations