Search in sources :

Example 61 with ResourceIdentifier

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

the class AmqpServiceBase method handleReceiverOpen.

/**
 * Handles a request from a client to establish a link for sending messages to this server.
 * The already established connection must have an authenticated user as principal for doing the authorization check.
 *
 * @param con the connection to the client.
 * @param receiver the receiver created for the link.
 */
protected void handleReceiverOpen(final ProtonConnection con, final ProtonReceiver receiver) {
    if (receiver.getRemoteTarget().getAddress() == null) {
        LOG.debug("client [container: {}] wants to open an anonymous link for sending messages to arbitrary addresses, closing link ...", con.getRemoteContainer());
        receiver.setCondition(ProtonHelper.condition(AmqpError.NOT_ALLOWED, "anonymous relay not supported"));
        receiver.close();
    } else {
        LOG.debug("client [container: {}] wants to open a link [address: {}] for sending messages", con.getRemoteContainer(), receiver.getRemoteTarget());
        try {
            final ResourceIdentifier targetResource = getResourceIdentifier(receiver.getRemoteTarget().getAddress());
            final AmqpEndpoint endpoint = getEndpoint(targetResource);
            if (endpoint == null) {
                handleUnknownEndpoint(con, receiver, targetResource);
            } else {
                final HonoUser user = Constants.getClientPrincipal(con);
                getAuthorizationService().isAuthorized(user, targetResource, Activity.WRITE).setHandler(authAttempt -> {
                    if (authAttempt.succeeded() && authAttempt.result()) {
                        Constants.copyProperties(con, receiver);
                        receiver.setTarget(receiver.getRemoteTarget());
                        endpoint.onLinkAttach(con, receiver, targetResource);
                    } else {
                        LOG.debug("subject [{}] is not authorized to WRITE to [{}]", user.getName(), targetResource);
                        receiver.setCondition(ProtonHelper.condition(AmqpError.UNAUTHORIZED_ACCESS.toString(), "unauthorized"));
                        receiver.close();
                    }
                });
            }
        } catch (final IllegalArgumentException e) {
            LOG.debug("client has provided invalid resource identifier as target address", e);
            receiver.setCondition(ProtonHelper.condition(AmqpError.NOT_FOUND, "no such address"));
            receiver.close();
        }
    }
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) HonoUser(org.eclipse.hono.auth.HonoUser)

Example 62 with ResourceIdentifier

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

the class RequestResponseEndpoint method handleMessage.

/**
 * Handles a request message received from a client.
 * <p>
 * The message gets rejected if
 * <ul>
 * <li>the message does not pass {@linkplain #passesFormalVerification(ResourceIdentifier, Message) formal verification}
 * or</li>
 * <li>the client is not {@linkplain #isAuthorized(HonoUser, ResourceIdentifier, Message) authorized to execute the operation}
 * indicated by the message's <em>subject</em> or</li>
 * <li>its payload cannot be parsed</li>
 * </ul>
 *
 * @param con The connection with the client.
 * @param receiver The link over which the message has been received.
 * @param targetAddress The address the message is sent to.
 * @param delivery The message's delivery status.
 * @param message The message.
 */
protected final void handleMessage(final ProtonConnection con, final ProtonReceiver receiver, final ResourceIdentifier targetAddress, ProtonDelivery delivery, Message message) {
    final Future<Void> formalCheck = Future.future();
    if (passesFormalVerification(targetAddress, message)) {
        formalCheck.complete();
    } else {
        formalCheck.fail(new AmqpErrorException(AmqpError.DECODE_ERROR, "malformed payload"));
    }
    final HonoUser clientPrincipal = Constants.getClientPrincipal(con);
    formalCheck.compose(ok -> isAuthorized(clientPrincipal, targetAddress, message)).compose(authorized -> {
        logger.debug("client [{}] is {}authorized to {}:{}", clientPrincipal.getName(), authorized ? "" : "not ", targetAddress, message.getSubject());
        if (authorized) {
            try {
                processRequest(message, targetAddress, clientPrincipal);
                ProtonHelper.accepted(delivery, true);
                return Future.succeededFuture();
            } catch (DecodeException e) {
                return Future.failedFuture(new AmqpErrorException(AmqpError.DECODE_ERROR, "malformed payload"));
            }
        } else {
            return Future.failedFuture(new AmqpErrorException(AmqpError.UNAUTHORIZED_ACCESS, "unauthorized"));
        }
    }).otherwise(t -> {
        if (t instanceof AmqpErrorException) {
            AmqpErrorException cause = (AmqpErrorException) t;
            MessageHelper.rejected(delivery, cause.asErrorCondition());
        } else {
            logger.debug("error processing request [resource: {}, op: {}]: {}", targetAddress, message.getSubject(), t.getMessage());
            MessageHelper.rejected(delivery, ProtonHelper.condition(AmqpError.INTERNAL_ERROR, "internal error"));
        }
        return null;
    });
}
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) HonoUser(org.eclipse.hono.auth.HonoUser) AmqpErrorException(org.eclipse.hono.util.AmqpErrorException) DecodeException(io.vertx.core.json.DecodeException)

Example 63 with ResourceIdentifier

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

the class AmqpServiceBase method handleSenderOpen.

/**
 * Handles a request from a client to establish a link for receiving messages from this server.
 *
 * @param con the connection to the client.
 * @param sender the sender created for the link.
 */
protected void handleSenderOpen(final ProtonConnection con, final ProtonSender sender) {
    final Source remoteSource = sender.getRemoteSource();
    log.debug("client [container: {}] wants to open a link [address: {}] for receiving messages", con.getRemoteContainer(), remoteSource);
    if (!ResourceIdentifier.isValid(remoteSource.getAddress())) {
        handleUnknownEndpoint(con, sender, remoteSource.getAddress());
        return;
    }
    final ResourceIdentifier targetResource = ResourceIdentifier.fromString(remoteSource.getAddress());
    final AmqpEndpoint endpoint = getEndpoint(targetResource);
    if (endpoint == null) {
        handleUnknownEndpoint(con, sender, targetResource.toString());
    } else {
        final HonoUser user = Constants.getClientPrincipal(con);
        getAuthorizationService().isAuthorized(user, targetResource, Activity.READ).onComplete(authAttempt -> {
            if (authAttempt.succeeded() && authAttempt.result()) {
                sender.setSource(sender.getRemoteSource());
                sender.setTarget(sender.getRemoteTarget());
                endpoint.onLinkAttach(con, sender, targetResource);
            } else {
                log.debug("subject [{}] is not authorized to READ from [{}]", user.getName(), targetResource);
                sender.setCondition(ProtonHelper.condition(AmqpError.UNAUTHORIZED_ACCESS.toString(), "unauthorized"));
                sender.close();
            }
        });
    }
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) HonoUser(org.eclipse.hono.auth.HonoUser) Source(org.apache.qpid.proton.amqp.transport.Source)

Example 64 with ResourceIdentifier

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

the class DelegatingCommandRouterAmqpEndpoint method processRegisterCommandConsumer.

/**
 * Processes a <em>register command consumer</em> request message.
 *
 * @param request The request message.
 * @param targetAddress The address the message is sent to.
 * @param spanContext The span context representing the request to be processed.
 * @return The response to send to the client via the event bus.
 */
protected Future<Message> processRegisterCommandConsumer(final Message request, final ResourceIdentifier targetAddress, final SpanContext spanContext) {
    final String tenantId = targetAddress.getTenantId();
    final String deviceId = MessageHelper.getDeviceId(request);
    final String adapterInstanceId = MessageHelper.getApplicationProperty(request.getApplicationProperties(), MessageHelper.APP_PROPERTY_ADAPTER_INSTANCE_ID, String.class);
    final Integer lifespanSecondsOrNull = MessageHelper.getApplicationProperty(request.getApplicationProperties(), MessageHelper.APP_PROPERTY_LIFESPAN, Integer.class);
    final Span span = TracingHelper.buildServerChildSpan(tracer, spanContext, SPAN_NAME_REGISTER_COMMAND_CONSUMER, getClass().getSimpleName()).start();
    final Future<Message> resultFuture;
    if (tenantId == null || deviceId == null || adapterInstanceId == null) {
        TracingHelper.logError(span, "missing tenant, device and/or adapter instance id");
        resultFuture = Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST));
    } else {
        final Duration lifespan = lifespanSecondsOrNull != null ? Duration.ofSeconds(lifespanSecondsOrNull) : Duration.ofSeconds(-1);
        TracingHelper.TAG_TENANT_ID.set(span, tenantId);
        TracingHelper.TAG_DEVICE_ID.set(span, deviceId);
        span.setTag(MessageHelper.APP_PROPERTY_ADAPTER_INSTANCE_ID, adapterInstanceId);
        span.setTag(MessageHelper.APP_PROPERTY_LIFESPAN, lifespan.getSeconds());
        logger.debug("register command consumer [tenant-id: {}, device-id: {}, adapter-instance-id {}, lifespan: {}s]", tenantId, deviceId, adapterInstanceId, lifespan.getSeconds());
        resultFuture = getService().registerCommandConsumer(tenantId, deviceId, adapterInstanceId, lifespan, span).map(res -> CommandRouterConstants.getAmqpReply(CommandRouterConstants.COMMAND_ROUTER_ENDPOINT, tenantId, request, res));
    }
    return finishSpanOnFutureCompletion(span, resultFuture);
}
Also used : HttpURLConnection(java.net.HttpURLConnection) DecodeException(io.vertx.core.json.DecodeException) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) CommandRouterConstants(org.eclipse.hono.util.CommandRouterConstants) HashMap(java.util.HashMap) ClientErrorException(org.eclipse.hono.client.ClientErrorException) AbstractDelegatingRequestResponseEndpoint(org.eclipse.hono.service.amqp.AbstractDelegatingRequestResponseEndpoint) MessageHelper(org.eclipse.hono.util.MessageHelper) Collectors(java.util.stream.Collectors) ServiceConfigProperties(org.eclipse.hono.config.ServiceConfigProperties) Future(io.vertx.core.Future) SpanContext(io.opentracing.SpanContext) Objects(java.util.Objects) JsonArray(io.vertx.core.json.JsonArray) List(java.util.List) Buffer(io.vertx.core.buffer.Buffer) Duration(java.time.Duration) Map(java.util.Map) Span(io.opentracing.Span) Message(org.apache.qpid.proton.message.Message) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) JsonObject(io.vertx.core.json.JsonObject) TracingHelper(org.eclipse.hono.tracing.TracingHelper) Message(org.apache.qpid.proton.message.Message) ClientErrorException(org.eclipse.hono.client.ClientErrorException) Duration(java.time.Duration) Span(io.opentracing.Span)

Example 65 with ResourceIdentifier

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

the class DelegatingCommandRouterAmqpEndpoint method processSetLastKnownGatewayRequest.

/**
 * Processes a <em>set last known gateway</em> request message.
 *
 * @param request The request message.
 * @param targetAddress The address the message is sent to.
 * @param spanContext The span context representing the request to be processed.
 * @return The response to send to the client via the event bus.
 */
protected Future<Message> processSetLastKnownGatewayRequest(final Message request, final ResourceIdentifier targetAddress, final SpanContext spanContext) {
    final String tenantId = targetAddress.getTenantId();
    final String deviceIdAppProperty = MessageHelper.getDeviceId(request);
    final String gatewayIdAppProperty = MessageHelper.getGatewayId(request);
    final Span span = TracingHelper.buildServerChildSpan(tracer, spanContext, SPAN_NAME_SET_LAST_GATEWAY, getClass().getSimpleName()).start();
    final Future<CommandRouterResult> resultFuture;
    if (tenantId == null) {
        TracingHelper.logError(span, "missing tenant");
        resultFuture = Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "missing tenant"));
    } else {
        if (deviceIdAppProperty != null && gatewayIdAppProperty != null) {
            logger.debug("setting last known gateway for tenant [{}], device [{}] to {}", tenantId, deviceIdAppProperty, gatewayIdAppProperty);
            TracingHelper.TAG_TENANT_ID.set(span, tenantId);
            TracingHelper.TAG_DEVICE_ID.set(span, deviceIdAppProperty);
            TracingHelper.TAG_GATEWAY_ID.set(span, gatewayIdAppProperty);
            if (MessageHelper.getPayloadSize(request) != 0) {
                logger.debug("ignoring payload in last known gateway request containing device/gateway properties");
            }
            resultFuture = getService().setLastKnownGatewayForDevice(tenantId, deviceIdAppProperty, gatewayIdAppProperty, span);
        } else if (MessageHelper.getPayloadSize(request) != 0) {
            TracingHelper.TAG_TENANT_ID.set(span, tenantId);
            final Buffer payload = MessageHelper.getPayload(request);
            resultFuture = parseSetLastKnownGatewayJson(payload).compose(deviceToGatewayMap -> {
                logger.debug("setting {} last known gateway entries for tenant [{}]", deviceToGatewayMap.size(), tenantId);
                span.log(Map.of("no_of_entries", deviceToGatewayMap.size()));
                return getService().setLastKnownGatewayForDevice(tenantId, deviceToGatewayMap, span);
            });
        } else {
            final String error = "either device_id and gateway_id application properties or alternatively a JSON payload must be set";
            TracingHelper.logError(span, error);
            resultFuture = Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, error));
        }
    }
    return finishSpanOnFutureCompletion(span, resultFuture.map(res -> CommandRouterConstants.getAmqpReply(CommandRouterConstants.COMMAND_ROUTER_ENDPOINT, tenantId, request, res)));
}
Also used : Buffer(io.vertx.core.buffer.Buffer) HttpURLConnection(java.net.HttpURLConnection) DecodeException(io.vertx.core.json.DecodeException) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) CommandRouterConstants(org.eclipse.hono.util.CommandRouterConstants) HashMap(java.util.HashMap) ClientErrorException(org.eclipse.hono.client.ClientErrorException) AbstractDelegatingRequestResponseEndpoint(org.eclipse.hono.service.amqp.AbstractDelegatingRequestResponseEndpoint) MessageHelper(org.eclipse.hono.util.MessageHelper) Collectors(java.util.stream.Collectors) ServiceConfigProperties(org.eclipse.hono.config.ServiceConfigProperties) Future(io.vertx.core.Future) SpanContext(io.opentracing.SpanContext) Objects(java.util.Objects) JsonArray(io.vertx.core.json.JsonArray) List(java.util.List) Buffer(io.vertx.core.buffer.Buffer) Duration(java.time.Duration) Map(java.util.Map) Span(io.opentracing.Span) Message(org.apache.qpid.proton.message.Message) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) JsonObject(io.vertx.core.json.JsonObject) TracingHelper(org.eclipse.hono.tracing.TracingHelper) ClientErrorException(org.eclipse.hono.client.ClientErrorException) Span(io.opentracing.Span)

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