use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class AbstractMessageSenderConnectionEventProducer method sendNotificationEvent.
private Future<Void> sendNotificationEvent(final Context context, final Device authenticatedDevice, final String protocolAdapter, final String remoteId, final String cause, final JsonObject data, final SpanContext spanContext) {
if (authenticatedDevice == null) {
// we only handle authenticated devices
return Future.succeededFuture();
}
final String tenantId = authenticatedDevice.getTenantId();
final String deviceId = authenticatedDevice.getDeviceId();
return context.getTenantClient().get(tenantId, spanContext).compose(tenant -> {
final JsonObject payload = new JsonObject();
payload.put("cause", cause);
payload.put("remote-id", remoteId);
payload.put("source", protocolAdapter);
if (data != null) {
payload.put("data", data);
}
return Optional.ofNullable(context.getMessageSenderClient()).map(client -> client.sendEvent(tenant, new RegistrationAssertion(deviceId), EventConstants.EVENT_CONNECTION_NOTIFICATION_CONTENT_TYPE, payload.toBuffer(), null, spanContext)).orElseGet(Future::succeededFuture);
});
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class AbstractProtocolAdapterBase method sendTtdEvent.
@Override
public final Future<Void> sendTtdEvent(final String tenant, final String deviceId, final Device authenticatedDevice, final Integer ttd, final SpanContext context) {
Objects.requireNonNull(tenant);
Objects.requireNonNull(deviceId);
Objects.requireNonNull(ttd);
final Future<RegistrationAssertion> tokenTracker = getRegistrationAssertion(tenant, deviceId, authenticatedDevice, context);
final Future<TenantObject> tenantConfigTracker = getTenantConfiguration(tenant, context);
return CompositeFuture.all(tokenTracker, tenantConfigTracker).compose(ok -> {
if (tenantConfigTracker.result().isAdapterEnabled(getTypeName())) {
final Map<String, Object> props = new HashMap<>();
props.put(MessageHelper.APP_PROPERTY_ORIG_ADAPTER, getTypeName());
props.put(MessageHelper.APP_PROPERTY_QOS, QoS.AT_LEAST_ONCE.ordinal());
props.put(MessageHelper.APP_PROPERTY_DEVICE_TTD, ttd);
return getEventSender(tenantConfigTracker.result()).sendEvent(tenantConfigTracker.result(), tokenTracker.result(), EventConstants.CONTENT_TYPE_EMPTY_NOTIFICATION, null, props, context).onSuccess(s -> log.debug("successfully sent TTD notification [tenant: {}, device-id: {}, TTD: {}]", tenant, deviceId, ttd)).onFailure(t -> log.debug("failed to send TTD notification [tenant: {}, device-id: {}, TTD: {}]", tenant, deviceId, ttd, t));
} else {
// this adapter is not enabled for the tenant
return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_FORBIDDEN));
}
});
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapter method doUploadCommandResponseMessage.
private Future<Void> doUploadCommandResponseMessage(final AmqpContext context, final ResourceIdentifier resource, final Span currentSpan) {
final Future<CommandResponse> responseTracker = Optional.ofNullable(getCommandResponse(context.getMessage())).map(Future::succeededFuture).orElseGet(() -> {
TracingHelper.logError(currentSpan, String.format("invalid message (correlationId: %s, address: %s, status: %s)", context.getMessage().getCorrelationId(), context.getMessage().getAddress(), MessageHelper.getStatus(context.getMessage())));
return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "malformed command response message"));
});
final Future<TenantObject> tenantTracker = getTenantConfiguration(resource.getTenantId(), currentSpan.context());
return CompositeFuture.all(tenantTracker, responseTracker).compose(ok -> {
final CommandResponse commandResponse = responseTracker.result();
log.trace("sending command response [device-id: {}, status: {}, correlation-id: {}, reply-to: {}]", resource.getResourceId(), commandResponse.getStatus(), commandResponse.getCorrelationId(), commandResponse.getReplyToId());
final Map<String, Object> items = new HashMap<>(3);
items.put(Fields.EVENT, "sending command response");
items.put(TracingHelper.TAG_CORRELATION_ID.getKey(), commandResponse.getCorrelationId());
items.put(MessageHelper.APP_PROPERTY_STATUS, commandResponse.getStatus());
currentSpan.log(items);
final Future<RegistrationAssertion> tokenFuture = getRegistrationAssertion(resource.getTenantId(), resource.getResourceId(), context.getAuthenticatedDevice(), currentSpan.context());
final Future<TenantObject> tenantValidationTracker = CompositeFuture.all(isAdapterEnabled(tenantTracker.result()), checkMessageLimit(tenantTracker.result(), context.getPayloadSize(), currentSpan.context())).map(success -> tenantTracker.result());
return CompositeFuture.all(tenantValidationTracker, tokenFuture).compose(success -> sendCommandResponse(tenantTracker.result(), tokenFuture.result(), commandResponse, currentSpan.context()));
}).map(delivery -> {
log.trace("forwarded command response from device [tenant: {}, device-id: {}]", resource.getTenantId(), resource.getResourceId());
metrics.reportCommand(Direction.RESPONSE, resource.getTenantId(), tenantTracker.result(), ProcessingOutcome.FORWARDED, context.getPayloadSize(), context.getTimer());
return delivery;
}).recover(t -> {
log.debug("cannot process command response from device [tenant: {}, device-id: {}]", resource.getTenantId(), resource.getResourceId(), t);
metrics.reportCommand(Direction.RESPONSE, resource.getTenantId(), tenantTracker.result(), ProcessingOutcome.from(t), context.getPayloadSize(), context.getTimer());
return Future.failedFuture(t);
});
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class AbstractHonoResource method getPutRequestDeviceAndAuth.
/**
* Gets a device identity for a CoAP PUT request which contains a tenant and device id in its URI.
*
* @param exchange The CoAP exchange with URI and/or peer's principal.
* @return A future indicating the outcome of the operation.
* The future will be succeeded if the device can be determined from the CoAP exchange,
* otherwise the future will be failed with a {@link ClientErrorException}.
*/
public Future<RequestDeviceAndAuth> getPutRequestDeviceAndAuth(final CoapExchange exchange) {
final List<String> pathList = exchange.getRequestOptions().getUriPath();
if (pathList.isEmpty()) {
return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "missing request URI"));
} else if (pathList.size() == 1) {
return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "missing tenant and device ID in URI"));
} else if (pathList.size() == 2) {
return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "missing device ID in URI"));
}
try {
final String[] path = pathList.toArray(new String[pathList.size()]);
final ResourceIdentifier identifier = ResourceIdentifier.fromPath(path);
final Device device = new Device(identifier.getTenantId(), identifier.getResourceId());
final Principal peer = exchange.advanced().getRequest().getSourceContext().getPeerIdentity();
if (peer == null) {
// unauthenticated device request
return Future.succeededFuture(new RequestDeviceAndAuth(device, null, null));
} else {
return TracingSupportingHonoResource.getAuthenticatedDevice(exchange).map(authenticatedDevice -> new RequestDeviceAndAuth(device, TracingSupportingHonoResource.getAuthId(exchange), authenticatedDevice));
}
} catch (final IllegalArgumentException cause) {
return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "invalid request URI"));
}
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class AbstractHonoResource method createCommandConsumer.
/**
* Creates a consumer for command messages to be sent to a device.
*
* @param ttdSecs The number of seconds the device waits for a command.
* @param tenantObject The tenant configuration object.
* @param deviceId The identifier of the device.
* @param gatewayId The identifier of the gateway that is acting on behalf of the device or {@code null} otherwise.
* @param context The device's currently executing CoAP request context.
* @param responseReady A future to complete once one of the following conditions are met:
* <ul>
* <li>the request did not include a <em>hono-ttd</em> query-parameter or</li>
* <li>a command has been received and the response ready future has not yet been completed or</li>
* <li>the ttd has expired</li>
* </ul>
* @param uploadMessageSpan The OpenTracing Span used for tracking the processing of the request.
* @return A future indicating the outcome of the operation.
* <p>
* The future will be completed with the created message consumer or {@code null}, if the response can be
* sent back to the device without waiting for a command.
* <p>
* The future will be failed with a {@code ServiceInvocationException} if the message consumer could not be
* created.
* @throws NullPointerException if any of the parameters other than TTD or gatewayId is {@code null}.
*/
protected final Future<CommandConsumer> createCommandConsumer(final Integer ttdSecs, final TenantObject tenantObject, final String deviceId, final String gatewayId, final CoapContext context, final Handler<AsyncResult<Void>> responseReady, final Span uploadMessageSpan) {
Objects.requireNonNull(tenantObject);
Objects.requireNonNull(deviceId);
Objects.requireNonNull(context);
Objects.requireNonNull(responseReady);
Objects.requireNonNull(uploadMessageSpan);
if (ttdSecs == null || ttdSecs <= 0) {
// no need to wait for a command
responseReady.handle(Future.succeededFuture());
return Future.succeededFuture();
}
final AtomicBoolean requestProcessed = new AtomicBoolean(false);
uploadMessageSpan.setTag(MessageHelper.APP_PROPERTY_DEVICE_TTD, ttdSecs);
final Span waitForCommandSpan = TracingHelper.buildChildSpan(getTracer(), uploadMessageSpan.context(), "create consumer and wait for command", getAdapter().getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).start();
final Handler<CommandContext> commandHandler = commandContext -> {
final Span processCommandSpan = TracingHelper.buildFollowsFromSpan(getTracer(), waitForCommandSpan.context(), "process received command").withTag(Tags.COMPONENT.getKey(), getAdapter().getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).addReference(References.FOLLOWS_FROM, commandContext.getTracingContext()).start();
Tags.COMPONENT.set(commandContext.getTracingSpan(), getAdapter().getTypeName());
commandContext.logCommandToSpan(processCommandSpan);
final Command command = commandContext.getCommand();
final Sample commandSample = getAdapter().getMetrics().startTimer();
if (isCommandValid(command, processCommandSpan)) {
if (requestProcessed.compareAndSet(false, true)) {
waitForCommandSpan.finish();
getAdapter().checkMessageLimit(tenantObject, command.getPayloadSize(), processCommandSpan.context()).onComplete(result -> {
if (result.succeeded()) {
addMicrometerSample(commandContext, commandSample);
// put command context to routing context and notify
context.put(CommandContext.KEY_COMMAND_CONTEXT, commandContext);
} else {
commandContext.reject(result.cause());
TracingHelper.logError(processCommandSpan, "rejected command for device", result.cause());
getAdapter().getMetrics().reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.from(result.cause()), command.getPayloadSize(), commandSample);
}
cancelCommandReceptionTimer(context);
setTtdStatus(context, TtdStatus.COMMAND);
responseReady.handle(Future.succeededFuture());
processCommandSpan.finish();
});
} else {
final String errorMsg = "waiting time for command has elapsed or another command has already been processed";
LOG.debug("{} [tenantId: {}, deviceId: {}]", errorMsg, tenantObject.getTenantId(), deviceId);
getAdapter().getMetrics().reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.UNDELIVERABLE, command.getPayloadSize(), commandSample);
commandContext.release(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, errorMsg));
TracingHelper.logError(processCommandSpan, errorMsg);
processCommandSpan.finish();
}
} else {
getAdapter().getMetrics().reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.UNPROCESSABLE, command.getPayloadSize(), commandSample);
LOG.debug("command message is invalid: {}", command);
commandContext.reject("malformed command message");
TracingHelper.logError(processCommandSpan, "malformed command message");
processCommandSpan.finish();
}
};
final Future<CommandConsumer> commandConsumerFuture;
if (gatewayId != null) {
// gateway scenario
commandConsumerFuture = getAdapter().getCommandConsumerFactory().createCommandConsumer(tenantObject.getTenantId(), deviceId, gatewayId, commandHandler, Duration.ofSeconds(ttdSecs), waitForCommandSpan.context());
} else {
commandConsumerFuture = getAdapter().getCommandConsumerFactory().createCommandConsumer(tenantObject.getTenantId(), deviceId, commandHandler, Duration.ofSeconds(ttdSecs), waitForCommandSpan.context());
}
return commandConsumerFuture.onFailure(thr -> {
TracingHelper.logError(waitForCommandSpan, thr);
waitForCommandSpan.finish();
}).map(consumer -> {
if (!requestProcessed.get()) {
// if the request was not responded already, add a timer for triggering an empty response
addCommandReceptionTimer(context, requestProcessed, responseReady, ttdSecs, waitForCommandSpan);
context.startAcceptTimer(vertx, tenantObject, getAdapter().getConfig().getTimeoutToAck());
}
// for unregistering the command consumer (which is something the parent request span doesn't wait for)
return new CommandConsumer() {
@Override
public Future<Void> close(final SpanContext ignored) {
final Span closeConsumerSpan = TracingHelper.buildFollowsFromSpan(getTracer(), waitForCommandSpan.context(), "close consumer").withTag(Tags.COMPONENT.getKey(), getAdapter().getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).start();
return consumer.close(closeConsumerSpan.context()).onFailure(thr -> TracingHelper.logError(closeConsumerSpan, thr)).onComplete(ar -> closeConsumerSpan.finish());
}
};
});
}
Aggregations