Search in sources :

Example 16 with ResourceIdentifier

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

the class AbstractVertxBasedMqttProtocolAdapter method uploadCommandResponseMessage.

/**
 * Uploads a command response message.
 *
 * @param ctx The context in which the MQTT message has been published.
 * @param targetAddress The address that the response should be forwarded to.
 * @return A future indicating the outcome of the operation.
 *         <p>
 *         The future will succeed if the message has been uploaded successfully.
 *         Otherwise, the future will fail with a {@link ServiceInvocationException}.
 * @throws NullPointerException if any of the parameters are {@code null}.
 */
public final Future<Void> uploadCommandResponseMessage(final MqttContext ctx, final ResourceIdentifier targetAddress) {
    Objects.requireNonNull(ctx);
    Objects.requireNonNull(targetAddress);
    final String[] addressPath = targetAddress.getResourcePath();
    Integer status = null;
    String reqId = null;
    final Future<CommandResponse> commandResponseTracker;
    if (addressPath.length <= CommandConstants.TOPIC_POSITION_RESPONSE_STATUS) {
        commandResponseTracker = Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "command response topic has too few segments"));
    } else {
        try {
            status = Integer.parseInt(addressPath[CommandConstants.TOPIC_POSITION_RESPONSE_STATUS]);
        } catch (final NumberFormatException e) {
            log.trace("got invalid status code [{}] [tenant-id: {}, device-id: {}]", addressPath[CommandConstants.TOPIC_POSITION_RESPONSE_STATUS], targetAddress.getTenantId(), targetAddress.getResourceId());
        }
        if (status != null) {
            reqId = addressPath[CommandConstants.TOPIC_POSITION_RESPONSE_REQ_ID];
            final CommandResponse commandResponse = CommandResponse.fromRequestId(reqId, targetAddress.getTenantId(), targetAddress.getResourceId(), ctx.message().payload(), ctx.contentType(), status);
            commandResponseTracker = commandResponse != null ? Future.succeededFuture(commandResponse) : Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "command response topic contains invalid data"));
        } else {
            // status code could not be parsed
            commandResponseTracker = Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "invalid status code"));
        }
    }
    final Span currentSpan = TracingHelper.buildChildSpan(tracer, ctx.getTracingContext(), "upload Command response", getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, targetAddress.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, targetAddress.getResourceId()).withTag(Constants.HEADER_COMMAND_RESPONSE_STATUS, status).withTag(Constants.HEADER_COMMAND_REQUEST_ID, reqId).withTag(TracingHelper.TAG_AUTHENTICATED.getKey(), ctx.authenticatedDevice() != null).start();
    final int payloadSize = Optional.ofNullable(ctx.message().payload()).map(Buffer::length).orElse(0);
    final Future<TenantObject> tenantTracker = getTenantConfiguration(targetAddress.getTenantId(), ctx.getTracingContext());
    return CompositeFuture.all(tenantTracker, commandResponseTracker).compose(success -> {
        final Future<RegistrationAssertion> deviceRegistrationTracker = getRegistrationAssertion(targetAddress.getTenantId(), targetAddress.getResourceId(), ctx.authenticatedDevice(), currentSpan.context());
        final Future<Void> tenantValidationTracker = CompositeFuture.all(isAdapterEnabled(tenantTracker.result()), checkMessageLimit(tenantTracker.result(), payloadSize, currentSpan.context())).mapEmpty();
        return CompositeFuture.all(deviceRegistrationTracker, tenantValidationTracker).compose(ok -> sendCommandResponse(tenantTracker.result(), deviceRegistrationTracker.result(), commandResponseTracker.result(), currentSpan.context()));
    }).compose(delivery -> {
        log.trace("successfully forwarded command response from device [tenant-id: {}, device-id: {}]", targetAddress.getTenantId(), targetAddress.getResourceId());
        metrics.reportCommand(Direction.RESPONSE, targetAddress.getTenantId(), tenantTracker.result(), ProcessingOutcome.FORWARDED, payloadSize, ctx.getTimer());
        // check that the remote MQTT client is still connected before sending PUBACK
        if (ctx.isAtLeastOnce() && ctx.deviceEndpoint().isConnected()) {
            currentSpan.log(EVENT_SENDING_PUBACK);
            ctx.acknowledge();
        }
        currentSpan.finish();
        return Future.<Void>succeededFuture();
    }).recover(t -> {
        TracingHelper.logError(currentSpan, t);
        currentSpan.finish();
        metrics.reportCommand(Direction.RESPONSE, targetAddress.getTenantId(), tenantTracker.result(), ProcessingOutcome.from(t), payloadSize, ctx.getTimer());
        return Future.failedFuture(t);
    });
}
Also used : HttpURLConnection(java.net.HttpURLConnection) LifecycleChange(org.eclipse.hono.notification.deviceregistry.LifecycleChange) ZonedDateTime(java.time.ZonedDateTime) DeviceChangeNotification(org.eclipse.hono.notification.deviceregistry.DeviceChangeNotification) MqttEndpoint(io.vertx.mqtt.MqttEndpoint) MqttPublishMessage(io.vertx.mqtt.messages.MqttPublishMessage) Tags(io.opentracing.tag.Tags) ProcessingOutcome(org.eclipse.hono.service.metric.MetricsTags.ProcessingOutcome) EndpointType(org.eclipse.hono.service.metric.MetricsTags.EndpointType) DeviceCredentials(org.eclipse.hono.adapter.auth.device.DeviceCredentials) Map(java.util.Map) Pair(org.eclipse.hono.util.Pair) AuthorizationException(org.eclipse.hono.adapter.AuthorizationException) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) Fields(io.opentracing.log.Fields) JsonObject(io.vertx.core.json.JsonObject) MqttConnectionException(io.vertx.mqtt.MqttConnectionException) ZoneOffset(java.time.ZoneOffset) TracingHelper(org.eclipse.hono.tracing.TracingHelper) AllDevicesOfTenantDeletedNotification(org.eclipse.hono.notification.deviceregistry.AllDevicesOfTenantDeletedNotification) AuthHandler(org.eclipse.hono.adapter.auth.device.AuthHandler) Futures(org.eclipse.hono.util.Futures) TenantServiceBasedX509Authentication(org.eclipse.hono.adapter.auth.device.TenantServiceBasedX509Authentication) Predicate(java.util.function.Predicate) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) CommandContext(org.eclipse.hono.client.command.CommandContext) MetricsTags(org.eclipse.hono.service.metric.MetricsTags) Set(java.util.Set) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) MessageHelper(org.eclipse.hono.util.MessageHelper) Collectors(java.util.stream.Collectors) Future(io.vertx.core.Future) Device(org.eclipse.hono.auth.Device) Objects(java.util.Objects) List(java.util.List) TenantTraceSamplingHelper(org.eclipse.hono.tracing.TenantTraceSamplingHelper) Buffer(io.vertx.core.buffer.Buffer) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) Optional(java.util.Optional) Span(io.opentracing.Span) NotificationEventBusSupport(org.eclipse.hono.notification.NotificationEventBusSupport) MqttQoS(io.netty.handler.codec.mqtt.MqttQoS) MqttConnectReturnCode(io.netty.handler.codec.mqtt.MqttConnectReturnCode) ConnectionLimitManager(org.eclipse.hono.adapter.limiting.ConnectionLimitManager) Command(org.eclipse.hono.client.command.Command) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) HashMap(java.util.HashMap) ClientErrorException(org.eclipse.hono.client.ClientErrorException) Deque(java.util.Deque) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) OptionalInt(java.util.OptionalInt) AtomicReference(java.util.concurrent.atomic.AtomicReference) Constants(org.eclipse.hono.util.Constants) ArrayList(java.util.ArrayList) DeviceUser(org.eclipse.hono.service.auth.DeviceUser) MqttServer(io.vertx.mqtt.MqttServer) HashSet(java.util.HashSet) CompositeFuture(io.vertx.core.CompositeFuture) SSLSession(javax.net.ssl.SSLSession) MqttTopicSubscription(io.vertx.mqtt.MqttTopicSubscription) AdapterConnectionsExceededException(org.eclipse.hono.adapter.AdapterConnectionsExceededException) LinkedList(java.util.LinkedList) CommandConstants(org.eclipse.hono.util.CommandConstants) TenantChangeNotification(org.eclipse.hono.notification.deviceregistry.TenantChangeNotification) ChainAuthHandler(org.eclipse.hono.adapter.auth.device.ChainAuthHandler) UsernamePasswordAuthProvider(org.eclipse.hono.adapter.auth.device.UsernamePasswordAuthProvider) CredentialsApiAuthProvider(org.eclipse.hono.adapter.auth.device.CredentialsApiAuthProvider) AbstractProtocolAdapterBase(org.eclipse.hono.adapter.AbstractProtocolAdapterBase) Direction(org.eclipse.hono.service.metric.MetricsTags.Direction) MqttServerOptions(io.vertx.mqtt.MqttServerOptions) Promise(io.vertx.core.Promise) MqttSubscribeMessage(io.vertx.mqtt.messages.MqttSubscribeMessage) ServerErrorException(org.eclipse.hono.client.ServerErrorException) Sample(io.micrometer.core.instrument.Timer.Sample) CommandResponse(org.eclipse.hono.client.command.CommandResponse) TenantObject(org.eclipse.hono.util.TenantObject) SpanContext(io.opentracing.SpanContext) ErrorHandlingMode(org.eclipse.hono.adapter.mqtt.MqttContext.ErrorHandlingMode) MqttUnsubscribeMessage(io.vertx.mqtt.messages.MqttUnsubscribeMessage) ChronoUnit(java.time.temporal.ChronoUnit) ConnectionAttemptOutcome(org.eclipse.hono.service.metric.MetricsTags.ConnectionAttemptOutcome) MemoryBasedConnectionLimitStrategy(org.eclipse.hono.adapter.limiting.MemoryBasedConnectionLimitStrategy) X509AuthProvider(org.eclipse.hono.adapter.auth.device.X509AuthProvider) DateTimeFormatter(java.time.format.DateTimeFormatter) ArrayDeque(java.util.ArrayDeque) Handler(io.vertx.core.Handler) DefaultConnectionLimitManager(org.eclipse.hono.adapter.limiting.DefaultConnectionLimitManager) CommandResponse(org.eclipse.hono.client.command.CommandResponse) Span(io.opentracing.Span) MqttEndpoint(io.vertx.mqtt.MqttEndpoint) TenantObject(org.eclipse.hono.util.TenantObject) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) ClientErrorException(org.eclipse.hono.client.ClientErrorException)

Example 17 with ResourceIdentifier

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

the class ErrorSubscription method validateTopic.

private static ResourceIdentifier validateTopic(final String topic) {
    Objects.requireNonNull(topic);
    if (!ResourceIdentifier.isValid(topic)) {
        throw new IllegalArgumentException("topic filter or its first segment must not be empty");
    }
    final ResourceIdentifier resource = ResourceIdentifier.fromString(topic);
    if (resource.length() != 4 || !"#".equals(resource.elementAt(resource.length() - 1))) {
        throw new IllegalArgumentException("invalid topic filter: must have 4 segments and end with '#'");
    }
    if (!isErrorEndpoint(resource.getEndpoint())) {
        throw new IllegalArgumentException("the endpoint needs to be '" + ERROR_ENDPOINT + "' or '" + ERROR_ENDPOINT_SHORT + "'");
    }
    return resource;
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier)

Example 18 with ResourceIdentifier

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

the class CommandSubscription method fromTopic.

/**
 * Creates a command subscription object for the given topic.
 * <p>
 * If the authenticated device is given, it is used to either validate the tenant and device-id
 * given via the topic or, if the topic doesn't contain these values, the authenticated device
 * is used to provide tenant and device-id for the created command subscription object.
 *
 * @param topic The topic to subscribe for commands.
 * @param qos The quality-of-service level for the subscription.
 * @param authenticatedDevice The authenticated device or {@code null}.
 * @return The CommandSubscription object or {@code null} if the topic does not match the rules.
 * @throws NullPointerException if topic or qos is {@code null}.
 */
public static CommandSubscription fromTopic(final String topic, final MqttQoS qos, final Device authenticatedDevice) {
    Objects.requireNonNull(topic);
    Objects.requireNonNull(qos);
    try {
        final ResourceIdentifier topicResource = validateTopic(topic);
        return new CommandSubscription(topicResource, authenticatedDevice, qos);
    } catch (final IllegalArgumentException e) {
        LOG.debug(e.getMessage());
        return null;
    }
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier)

Example 19 with ResourceIdentifier

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

the class VertxBasedMqttProtocolAdapter method mapTopic.

Future<ResourceIdentifier> mapTopic(final MqttContext context) {
    final Promise<ResourceIdentifier> result = Promise.promise();
    final ResourceIdentifier topic = context.topic();
    final MqttQoS qos = context.message().qosLevel();
    switch(MetricsTags.EndpointType.fromString(topic.getEndpoint())) {
        case TELEMETRY:
            if (MqttQoS.EXACTLY_ONCE.equals(qos)) {
                // client tries to send telemetry message using QoS 2
                result.fail(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "QoS 2 not supported for telemetry messages"));
            } else {
                result.complete(topic);
            }
            break;
        case EVENT:
            if (MqttQoS.AT_LEAST_ONCE.equals(qos)) {
                result.complete(topic);
            } else {
                // client tries to send event message using QoS 0 or 2
                result.fail(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "Only QoS 1 supported for event messages"));
            }
            break;
        case COMMAND:
            if (MqttQoS.EXACTLY_ONCE.equals(qos)) {
                // client tries to send control message using QoS 2
                result.fail(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "QoS 2 not supported for command response messages"));
            } else {
                result.complete(topic);
            }
            break;
        default:
            // MQTT client is trying to publish on a not supported endpoint
            log.debug("no such endpoint [{}]", topic.getEndpoint());
            result.fail(new ClientErrorException(HttpURLConnection.HTTP_NOT_FOUND, "no such endpoint"));
    }
    return result.future();
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) ClientErrorException(org.eclipse.hono.client.ClientErrorException) MqttQoS(io.netty.handler.codec.mqtt.MqttQoS)

Example 20 with ResourceIdentifier

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

the class DelegatingTenantAmqpEndpoint method isAuthorized.

/**
 * Checks if the client is authorized to invoke an operation.
 * <p>
 * If the request does not include a <em>tenant_id</em> application property
 * then the request is authorized by default. This behavior allows clients to
 * invoke operations that do not require a tenant ID as a parameter.
 * <p>
 * If the request does contain a tenant ID parameter in its application properties
 * then this tenant ID is used for the authorization check together with the
 * endpoint and operation name.
 *
 * @param clientPrincipal The client.
 * @param resource The resource the operation belongs to.
 * @param request 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 request) {
    Objects.requireNonNull(request);
    final String tenantId = MessageHelper.getTenantId(request);
    if (tenantId == null) {
        // delegate authorization check to filterResource operation
        return Future.succeededFuture(Boolean.TRUE);
    } else {
        final ResourceIdentifier specificTenantAddress = ResourceIdentifier.fromPath(new String[] { resource.getEndpoint(), tenantId });
        return getAuthorizationService().isAuthorized(clientPrincipal, specificTenantAddress, request.getSubject());
    }
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier)

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